<!--
 Copyright (c) 2023, donnie <donnie4w@gmail.com>
 All rights reserved.
 Use of this source code is governed by The GNU Affero General Public License
 that can be found in the LICENSE file.

 https://github.com/donnie4w/tim
 https://github.com/donnie4w/webtim
 https://tlnet.top/tim
 https://tlnet.top/webtim
 
 该项目用于学习tim的相关用法，请勿将项目或相关代码作商用
-->
<html lang="en">

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="/assets/plugins/perfect-scrollbar/css/perfect-scrollbar.css" rel="stylesheet">
	<link href="/assets/plugins/metismenu/css/metisMenu.min.css" rel="stylesheet">
	<link rel="shortcut icon" href="/favicon.ico">
	<link rel="bookmark" href="/favicon.ico">
	<script src="/assets/js/jquery-3.7.1.min.js"></script>
	<script src="/assets/js/bootstrap.bundle.min.js"></script>
	<script src="/assets/js/t.js"></script>
	<script src="/assets/js/pako.es5.js"></script>
	<script src="/assets/plugins/perfect-scrollbar/js/perfect-scrollbar.js"></script>
	<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
	<link href="/assets/css/app.css" rel="stylesheet">
	<link href="/assets/css/icons.css" rel="stylesheet">
	<script src="/assets/js/util.js"></script>
	<script src="/assets/js/app.js"></script>
	<script src="/assets/js/timjs.js"></script>
	<script src="/assets/js/RecordRTC.js"></script>
	<script src="/assets/js/videojoin.js"></script>
	<link rel="stylesheet" href="/assets/css/dark-theme.css">
	<link rel="stylesheet" href="/assets/css/semi-dark.css">
	<link rel="stylesheet" href="/assets/css/header-colors.css">
	<title>webtim</title>
</head>

<body class="container-fluid card">
	<div class="top-menu ms-auto">
		<ul class="navbar-nav align-items-center">
			<li class="nav-item dropdown dropdown-large">
				<div class="dropdown-menu dropdown-menu-end">
					<div class="header-notifications-list">
					</div>
				</div>
			</li>
			<li class="nav-item dropdown dropdown-large">
				<div class="dropdown-menu dropdown-menu-end">
					<div class="header-message-list">
					</div>
				</div>
			</li>
		</ul>
	</div>
	<div>
		<div class="container" id="toastshow"></div>
		<div class="col-lg-12 card p-lg-1 rounded-0">
			<div>
				&nbsp;<img src="/img/user.png" id="loginicon" width="30" height="30" onclick="modaltimselfshow()"
					class="rounded-circle" alt="">
				<button type="button" class="btn btn-light mt-1" id="modal-btn-control" data-bs-toggle="popover"
					data-bs-trigger="hover" data-bs-content="控制面板" data-bs-placement="bottom"
					style="font-size: 20px;">&#128187;</button>
				<button type="button" class="btn btn-light mt-1" id="modal-btn-group" data-bs-toggle="popover"
					data-bs-trigger="hover" data-bs-content="我的群" data-bs-placement="bottom"
					style="font-size: 20px;">&#128246;</button>
				<button type="button" class="btn btn-light mt-1" id="modal-btn-webtimuser" data-bs-toggle="popover"
					data-bs-trigger="hover" data-bs-content="加好友" data-bs-placement="bottom"
					style="font-size: 20px;">&#10133;</button>
				<button type="button" class="btn btn-light mt-1" id="modal-btn-timgroup" data-bs-toggle="popover"
					data-bs-trigger="hover" data-bs-content="群组" data-bs-placement="bottom"
					style="font-size: 20px;">&#128101;</button>
				<button type="button" class="btn btn-light mt-1" onclick="timmyfriendshow()" data-bs-toggle="popover"
					data-bs-trigger="hover" data-bs-content="我的好友" data-bs-placement="bottom"
					style="font-size: 20px;">&#128100;</button>
				<button type="button" class="btn btn-light mt-1" id="modal-btn-timlivestream" data-bs-toggle="popover"
					data-bs-trigger="hover" data-bs-content="直播间" data-bs-placement="bottom"
					style="font-size: 20px;">&#127910;</button>
				<button type="button" class="btn btn-light mt-1" id="modal-btn-timliveroomstream"
					data-bs-toggle="popover" data-bs-trigger="hover" data-bs-content="多人实时对话" data-bs-placement="bottom"
					style="font-size: 20px;">&#127760;</button>
			</div>
		</div>
	</div>


	<div class="modal fade" id="modal-login">
		<div class="modal-dialog modal-md">
			<div class="modal-content">
				<div class="modal-header">
					<h4>tim通讯平台</h4>
					<button type="button" class="modal-title btn btn-light" onclick="modalregister()">注册账号</button>
				</div>

				<div class="modal-body">
					<form class="row g-3" id="loginform">
						<div class="col-12">
							<label for="inputLastEnterUsername" class="form-label">登录账号</label>
							<div class="input-group input-group-lg"> <span
									class="input-group-text bg-transparent">&#128221;</span>
								<input type="text" class="form-control border-start-0" id="username" name="username"
									placeholder="输入登录账号" />
							</div>
						</div>

						<div class="col-12">
							<label for="inputLastEnterPassword" class="form-label">密码</label>
							<div class="input-group input-group-lg"> <span
									class="input-group-text bg-transparent">&#128271;</span>
								<input type="password" class="form-control border-start-0" id="password" name="password"
									placeholder="登录密码" />
							</div>
						</div>
					</form>

					<div class="col-12">
						<div class="d-grid p-2">
							<button class="btn btn-dark btn-lg px-5" onclick="login()">&#127759;登录</button>
						</div>
						<div class="text-center">
							<a href="javascript:;" onclick="technologyshow()">webtim技术说明</a>
							<p>Copyright © 2023 webtim.cn <a href="https://beian.miit.gov.cn" target="_blank">粤ICP备2023007197号</a></p>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-control">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="controlclose();">关闭</button>
					<h4 class="modal-title">&#128187;控制面板</h4>
					<button type="button" class="modal-title btn btn-primary" onclick="logout()" id="logoutId"
						style="display: none;">退出登录</button>
				</div>
				<div class="modal-body">
					<button type="button" class="btn btn-light mt-1" onclick="technologyshow()">&#128221;技术说明</button>
					<button type="button" class="btn btn-light mt-1" onclick="modaltimselfshow()">&#127748;个人资料</button>
					<button type="button" class="btn btn-light mt-1" id="modal-btn-register">&#127381;注册新账户</button>
					<button type="button" class="btn btn-light mt-1" onclick="timmyfriendshow()">&#128100;我的好友</button>
					<button type="button" class="btn btn-light mt-1"
						onclick="modalmutlivideoshow()">&#127760;多人实时对话</button>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="controlclose();">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-technology">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="technologyclose();">关闭</button>
					<h4 class="modal-title">webtim技术说明</h4>
				</div>
				<div class="modal-body  bg-white">
					<div class="col-lg-12" style="height: calc(100vh - 4rem);">
						<iframe src="https://tlnet.top/blogs/modal_control" style="width: 100%;height: 100%;"></iframe>
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="technologyclose();">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-timself">
		<div class="modal-dialog modal-md">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="timselfclose()">关闭</button>
					<h4 class="modal-title">我的</h4>
				</div>
				<div class="modal-body">
					<div class="row" id="timselflist">
						<h3>未登录</h3>
					</div>
					<div class="p-1" id="modifyPwdcard" style="display: none;">
						<hr />
						<form id="modifyPwdForm">
							<div class="col-12">
								<label for="inputLastEnterUsername" class="form-label">原密码</label>
								<div class="input-group input-group-lg"> <span
										class="input-group-text bg-transparent">&#128272;</span>
									<input type="password" class="form-control border-start-0" id="oldpassword"
										placeholder="旧密码" />
								</div>
							</div>
							<div class="col-12">
								<label for="inputLastEnterPassword" class="form-label">新密码</label>
								<div class="input-group input-group-lg"> <span
										class="input-group-text bg-transparent">&#128271;</span>
									<input type="password" class="form-control border-start-0" id="newpassword"
										placeholder="新密码" />
								</div>
							</div>
						</form>
						<div class="col-12">
							<div class="btn-group">
								<button type="button" class="btn btn-primary" onclick="unmodifyPwd()">
									取消</button>&nbsp;
								<button type="button" class="btn btn-primary" onclick="modifyPwd()">
									确定修改</button>
							</div>
						</div>
					</div>
					<div class="p-1" id="modifyUserinfocard" style="display: none;">
						<hr />
						<form class="row g-3" id="modifyuserinfoform">
							<div class="col-12">
								<input type="text" id="modifytoken" name="token" hidden />
								<label for="inputLastEnterUsername" class="form-label">昵称</label>
								<div class="input-group input-group-lg"> <span
										class="input-group-text bg-transparent">&#127875;</span>
									<input type="text" class="form-control border-start-0" id="modifynick" name="nick"
										placeholder="昵称" />
								</div>
							</div>
							<div class="d-flex">
								<div>
									<b>[头像]</b><img src="/img/1.jpg" id="headcover" width="100px" height="100px">
								</div>
								<div>
									<input type="text" value="/img/1.jpg" id="headcover2" name="logo" hidden />
								</div>
								<div>&nbsp;&nbsp;&nbsp;&nbsp;</div>
								<div width="100px" height="100px">
									<input style="opacity: 0;" width="100px" height="100px" type="file" id="coverinputs"
										name="coverfile" />
									<span class="row text-center" id="updateSelfcover">
										<svg class="bi bi-person-circle" width="2em" height="2em" viewBox="0 0 16 16"
											fill="currentColor" xmlns="http://www.w3.org/2000/svg">
											<path
												d="M13.468 12.37C12.758 11.226 11.195 10 8 10s-4.757 1.225-5.468 2.37A6.987 6.987 0 0 0 8 15a6.987 6.987 0 0 0 5.468-2.63z" />
											<path fill-rule="evenodd" d="M8 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" />
											<path fill-rule="evenodd"
												d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zM0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8z" />
										</svg>
										<h6 id='upcover' style="font-size: small;color: rgb(34, 95, 249);">[上传头像]
										</h6>
									</span>
								</div>
							</div>
							<div class="col-12" id="headcoverlist">
							</div>
						</form>
						<div class="col-12">
							<div class="btn-group">
								<button type="button" class="btn btn-primary" onclick="unmodifyUserinfo()">
									取消</button>&nbsp;
								<button type="button" class="btn btn-primary" onclick="modifyUserinfo()">
									确定修改</button>
							</div>
						</div>
					</div>
					<hr />
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-webtimuser">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="friend-close">关闭</button>
					<h4 class="modal-title">&#10133;webtim已注册用户</h4>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 400px;" id="accountlist">
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="friend-close-end">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-group">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="group-close">关闭</button>
					<h4 class="modal-title">我的群</h4>
					<button type="button" class="modal-title btn btn-primary" id="modal-btn-newgroup">新建群</button>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 400px;" id="mygrouplist">
					</div>
				</div>
				<div class="modal-footer">
					<span>&#128308;我建立的群</span>
					<button type="button" class="btn btn-default" data-dismiss="modal" id="group-close-end">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-timgroup">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="timgroup-close">关闭</button>
					<h4 class="modal-title">webtim群组</h4>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 400px;" id="timgrouplist">
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-success" data-dismiss="modal">公有群</button>
					<button type="button" class="btn btn-danger" data-dismiss="modal">私有群</button>
					<button type="button" class="btn btn-default" data-dismiss="modal"
						id="timgroup-close-end">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-mutlivideo">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="modalmutlivideoclose()">关闭</button>
					<h4 class="modal-title fs-normal">实时音视频</h4>
					<button type="button" class="btn btn-primary btn-sm" data-dismiss="modal"
						onclick="mutlivideoroomshow()">新建</button>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 400px;" id="mygrouplist_v">
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="modalmutlivideoclose()">关闭</button>
				</div>
			</div>
		</div>
	</div>


	<div class="modal fade" id="modal-mutlivideostream">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-light" data-dismiss="modal"
						onclick="mutlivideostreamhide()">&#10134;</button>
					<h4 id="videoroomname"></h4>
					<button type="button" class="btn btn-danger" data-dismiss="modal"
						onclick="mutlivideostreamclose()">退出</button>
				</div>
				<div class="modal-body">
					<input type="text" id="mutlivideostreamid" hidden />
					<div class="row p-1 bg-light" id="mutlivideostreamlist"></div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-light" data-dismiss="modal"
						onclick="switchovervideo()">&#127909;视频</button>
					<button type="button" class="btn btn-light" data-dismiss="modal"
						onclick="switchoveraudio()">&#128222;语音</button>
					<button type="button" class="btn btn-light" data-dismiss="modal"
						onclick="mutlivideostreamhide()">&#10134;最小化</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-timmyfriend">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="modalmyfriend-close"
						onclick="timmyfriendclose()">关闭</button>
					<h4 class="modal-title">我的好友</h4>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 400px;" id="modalmyfriendlist">
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="modalmyfriend-close-end"
						onclick="timmyfriendclose()">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-register">
		<div class="modal-dialog modal-md">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="register-close">关闭</button>
					<h4 class="modal-title">新用户注册</h4>
				</div>
				<div class="modal-body">
					<div class="wrapper">
						<div class="row">
							<div class="col-xl-11 mx-auto">
								<div class="card border-top border-0 border-4 border-dark">
									<div class="card-body p-1">
										<form class="row g-3" id="registerform">
											<div class="col-12">
												<label for="inputLastEnterUsername" class="form-label">登录账号</label>
												<div class="input-group input-group-lg"> <span
														class="input-group-text bg-transparent">&#128221;</span>
													<input type="text" class="form-control border-start-0"
														id="registername" name="loginname" placeholder="输入登录账号" />
												</div>
											</div>

											<div class="col-12">
												<label for="inputLastEnterPassword" class="form-label">密码</label>
												<div class="input-group input-group-lg"> <span
														class="input-group-text bg-transparent">&#128271;</span>
													<input type="password" class="form-control border-start-0"
														id="registerpwd" name="password" placeholder="登录密码" />
												</div>
											</div>
											<div class="col-12">
												<label for="inputLastEnterUsername" class="form-label">昵称</label>
												<div class="input-group input-group-lg"> <span
														class="input-group-text bg-transparent">&#127875;</span>
													<input type="text" class="form-control border-start-0" name="nick"
														id="registerNick" placeholder="昵称" />
												</div>
											</div>
											<div class="col-12">
												<b>[头像]</b><img src="/img/1.jpg" id="rheadcover" width="100px"
													height="100px">
												<input type="text" value="/img/1.jpg" id="rheadcover2" name="logo"
													hidden />
											</div>
											<div class="col-12" id="rheadcoverlist">
											</div>
										</form>
									</div>
									<div class="col-12">
										<div class="d-grid">
											<button class="btn btn-dark btn-lg px-5" onclick="register()">
												确定注册</button>
										</div>
									</div>
									<hr />
								</div>
							</div>
						</div>
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						id="register-close-end">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-newgroup">
		<div class="modal-dialog modal-md">
			<div class="modal-content bg-light">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="newgroup-close">x
						关闭</button>
				</div>
				<div class="wrapper">
					<div class="col-xl-11 mx-auto">
						<div class="card border-top border-0 border-4 border-dark">
							<div class="card-body p-1">
								<form class="row g-3" id="newgroupform">
									<div class="col-12">
										<div class="input-group input-group-lg"> <span
												class="input-group-text bg-transparent">&#128246;</span>
											<input type="text" id="newgrouptoken" name="token" hidden />
											<input type="text" class="form-control border-start-0" name="topic"
												placeholder="群主题" id="grouptopic" />
										</div>
										<input type="radio" name="gtype" value="1" />私有群
										<input type="radio" name="gtype" value="2" checked />公有群
									</div>
									<div class="col-12">
										<b>[群封面]</b><img src="/img/001.jpg" id="groupcover" width="100px"
											height="100px">
										<input type="text" value="/img/001.jpg" id="groupcover2" name="logo" hidden />
									</div>
									<div class="col-12" id="groupcoverlist">
									</div>
							</div>
							</form>
							<div class="col-12">
								<div class="d-grid">
									<button class="btn btn-primary btn-md px-1" onclick="newgroup()">新建群</button>
								</div>
							</div>
							<hr />
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-groupmember">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						id="groupmember-close">关闭</button>
					<h4 class="modal-title" id="roomid"></h4>
					<input id="groupmember_node" hidden />
					<button type="button" class="btn btn-primary" data-dismiss="modal" onclick="leaveroom()">退群</button>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 400px;" id="groupmemberlist">
					</div>
				</div>
				<div class="modal-footer">
					<div class="btn-group">
						<button type="button" class="btn btn-sm  btn-warning">好友</button>
						<button type="button" class="btn btn-sm  btn-info">我</button>
						<button type="button" class="btn btn-sm  btn-success">群主</button>
						<button type="button" class="btn btn-sm  btn-primary">加好友</button>
						<button type="button" class="btn btn-sm  btn-danger">踢出群</button>
					</div>
					<button type="button" class="btn btn-default" data-dismiss="modal"
						id="groupmember-close-end">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-timlivestream">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" id="modalmyfriend-close"
						onclick="timlivestreamclose()">&#10060;关闭</button>
					<!-- <h4 class="modal-title">&#127910;</h4> -->
					<span>
						<button type="button" class="btn btn-default btn-sm" onclick="flushnewlive()"
							style="font-size:x-large;">
							<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor"
								class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
								<path fill-rule="evenodd"
									d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
								<path
									d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
							</svg>刷新
						</button>
						<button type="button" class="modal-title btn btn-primary" data-dismiss="modal"
							onclick="opennewlive()">我要直播</button>
					</span>
				</div>
				<div class="modal-body">
					<div class="row" style="overflow: scroll;height: 450px;" id="modalvideolivelist">
					</div>
				</div>
				<div class="modal-footer">
					<h6>&#128309;个人视频直播</h6>
					<h6>&#128308;播放视频直播</h6>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-timvideo">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="timvideoclose()">关闭</button>
					<h2 id="readylink" value="1"></h2>
					<h4 class="modal-title">视频</h4>
				</div>
				<div class="modal-body" style="overflow: scroll;height: 400px;">
					<div class="d-flex flex-column bg-light mb-1" id="timvideoview">
						<video class="p-1" id="myvideo" autoplay style="width: 150px;height: auto;" muted>
						</video>
						<div class="p-1" id="videophone"></div>
					</div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="timvideoclose()">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-newlive">
		<div class="modal-dialog modal-lg">
			<div class="modal-content bg-light">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="closenewlive()">x关闭</button>
				</div>
				<div class="modal-body" style="display: inline;" id="videowindow1">
					<div class="card border-top border-0 border-4 border-dark">
						<div class="card-body p-1">
							<form class="row g-3" id="newliveform">
								<div class="col-12">
									<div class="input-group input-group-lg"> <span
											class="input-group-text bg-transparent">&#127751;</span>
										<input type="text" id="livestreamform_node" name="node" hidden />
										<input type="text" id="livestreamform_vnode" name="vnode" hidden />
										<input type="text" id="livestreamform_token" name="token" hidden />
										<input type="text" id="livestreamform_cover" name="cover" hidden />
										<input type="text" id="livestreamform_opera" name="opera" hidden />
										<input type="text" id="livestreamform_topic" class="form-control border-start-0"
											name="topic" placeholder="直播主题" />
									</div>
									<input type="radio" id="livetype" name="ltype" onchange="selectfile(this)" value="1"
										checked />个人视频直播
									<input type="radio" id="livetypefile" name="ltype" onchange="selectfile(this)"
										value="2" id="" />播放视频直播
								</div>
								<div class="col-12">
									<h6><b>说明：</b></h6>
									<p>[个人视频直播]将调用设备摄像头与麦克风进行现场直播</p>
									<p>[播放视频直播]将选择系统视频文件播放并直播播放视频流</p>
								</div>
							</form>
							<div class="col-12" id="videofilediv" hidden>
								<span>
									<input type="checkbox" id="fileloop" />&#128257;<input type="file" id="videofile"
										onchange="selectvideofile()" />
								</span>
							</div>
						</div>
						<div class="col-12">
							<div class="d-grid">
								<button class="btn btn-primary btn-md px-1" onclick="newlivestart()">开始直播</button>
							</div>
						</div>
					</div>
				</div>
				<div class="wrapper" style="display: none;" id="videowindow2">
					<div class="card d-flex ratio ratio-21x9" id="playvideowindow">
					</div>
					<div class="modal-footer">
						<div class="col-12">
							<div class="d-grid">
								<div class="btn-group">
									<button class="btn btn-primary btn-md px-1" onclick="livefullscreen()">最大化</button>
									<button class="btn btn-primary btn-md px-1" onclick="livestop()">关闭直播</button>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-videostream">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<h5 id="videostreamtitle"></h5>
				</div>
				<div class="modal-body">
					<div class="flex ratio ratio-21x9" id="videostreamdiv">
					</div>
				</div>
				<div class="modal-footer">
					<div class="col-12">
						<div class="d-grid">
							<div class="btn-group">
								<button class="btn btn-primary btn-md" onclick="videostreamfullscreen()">最大化</button>
								<button class="btn btn-primary btn-md px-1" onclick="videostreamclose()">关闭</button>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-newvideogroup">
		<div class="modal-dialog modal-md">
			<div class="modal-content bg-light">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal" onclick="mutlivideoroomclose()">x
						关闭</button>
					<h4>多人实时对话群</h4>
				</div>
				<div class="wrapper">
					<div class="col-xl-11 mx-auto">
						<div class="card border-top border-0 border-4 border-dark">
							<div class="card-body p-1">
								<form class="row g-3" id="newgroupform_v">
									<div class="col-12">
										<div class="input-group input-group-lg"> <span
												class="input-group-text bg-transparent">&#9210;</span>
											<input type="text" id="newgrouptoken_v" name="token" hidden />
											<input type="text" name="mode" value="1" hidden />
											<input type="text" class="form-control border-start-0" name="topic"
												placeholder="群主题" id="grouptopic_v" />
										</div>
									</div>
									<div class="col-12">
										<b>[群封面]</b><img src="/img/101.jpg" id="groupcover_v" width="100px"
											height="100px">
										<input type="text" value="/img/101.jpg" id="groupcover2_v" name="logo" hidden />
									</div>
									<div class="col-12" id="groupcoverlist_v"></div>
							</div>
							</form>
							<div class="col-12">
								<div class="d-grid">
									<button class="btn btn-primary btn-md px-1" onclick="newvideogroup()">新建群</button>
								</div>
							</div>
							<hr />
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-sendfile">
		<div class="modal-dialog modal-md">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-light" data-dismiss="modal"
						onclick="sendfileclose()">x关闭</button>
					<img src="/img/user.png" class="rounded-circle" width="40" height="40" id="sendfilehead" />
					<input type="text" id="recvnode" hidden>
					<input type="text" id="vnode" hidden>
					<h4 class="modal-title">传文件</h4>
				</div>
				<div class="modal-body" height="300px" width="500px">
					<input type="file" id="transfileId">

				</div>
				<div class="modal-footer">
					<button class="btn btn-primary" onclick="showsendToast()">提示框</button>
					<button class="btn btn-primary" onclick="sendfile()">发送</button>
				</div>
			</div>
		</div>
	</div>

	<div class="modal fade" id="modal-userinfo">
		<div class="modal-dialog modal-md">
			<form id="userinfo_form">
				<input type="text" id="userinfo_token" name="token" />
			</form>
		</div>
	</div>

	<div class="modal fade" id="modal-videopullmember">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="modalvideopullmemberclose()">x关闭</button>
					<h4 class="modal-title">添加群成员</h4>
					<button type="button" class="btn btn-primary" data-dismiss="modal"
						onclick="submitpullmember()">确定</button>
				</div>
				<div class="modal-body">
					<input type="text" id="videopullmemberAccount" hidden />
					<div class="row" style="overflow: scroll;height: 400px;" id="modvideopullmemberlist"></div>
				</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal"
						onclick="modalvideopullmemberclose()">关闭</button>
				</div>
			</div>
		</div>
	</div>

	<div class="card rounded-0">
		<div class="chat-wrapper">
			<div class="chat-sidebar">
				<div class="chat-sidebar-header">
					<div class="d-flex align-items-center">
						<div class="chat-user-online" onclick="hideSidebar()">
							<img src="/img/user.png" id="icon" width="45" height="45" class="rounded-circle" alt="">
						</div>
						<div class="flex-grow-1 ms-2">
							<p class="mb-0" id="loginflag">未登录</p>
						</div>
						<div onclick="hideSidebar()">
							<div class="chat-toggle-btn">&lt;</div>
						</div>
					</div>
				</div>
				<div class="chat-sidebar-content">
					<div class="tab-content" id="pills-tabContent">
						<div class="tab-pane fade show active" id="pills-Chats">
							<div class="chat-list ps ps--active-y">
								<div class="list-group list-group-flush" id="msgnodelist">
								</div>
							</div>
						</div>
					</div>
				</div>
				<!-- <div class="chat-footer" id="chat" onclick="hideSidebar()">
					<div class="chat-toggle-btn">&lt;</div>
				</div> -->
			</div>
			<div class="chat-header d-flex align-items-center ">
				<div>
					<div class="chat-toggle-btn">&gt;</div>
				</div>
				<div class="input-group mb-2">
					<h6 onclick="hideSidebar()"></h6>
					<input hidden type="text" class="form-control" placeholder="账号" id="accountId" />
				</div>
				<div>
					<span class="input-group-text text-primary bg-white" id="touser" style="border: none;"></span>
				</div>
				<div>
					<h4 class="mb-1 font-weight-bold"><img src="/img/user.png" id="iconuser" width="60" height="60"
							class="rounded-circle"></h4>
				</div>
				<div style="position: absolute; top: 1%;right: 1%;max-width: 200px;">
					<div id="sendfiletoast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"
						data-bs-autohide="false">
						<div class="toast-header">
							<strong class="me-auto">文件传输</strong>
							<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
						</div>
						<div class="toast-body col-12">
							<span>
								<h6 id="rateid"></h6>
							</span>
							<span id="sendfileshow"></span>
						</div>
					</div>
				</div>
			</div>
			<div class="chat-content ps ps--active-y" id="containerId">
			</div>

			<div class="modal fade" id="lastItemModal" tabindex="-1" aria-labelledby="lastItemModalLabel"
				aria-hidden="true" style="display: none;">
				<div class="modal-dialog modal-dialog-centered">
					<div class="modal-content" style="background-color:rgba(0, 0, 0, 0);border: none;">
						<div class="modal-body  d-flex justify-content-center align-items-center">
							已经是最后一条数据
						</div>
					</div>
				</div>
			</div>
			<div class="modal fade" id="dataLoadingModal" tabindex="-1" aria-labelledby="dataLoadingModalLabel"
				aria-hidden="true" style="display: none;">
				<div class="modal-dialog modal-dialog-centered">
					<div class="modal-content" style="background-color:rgba(0, 0, 0, 0);border: none;">
						<div class="modal-body d-flex justify-content-center align-items-center">
							<div class="spinner-border text-primary" role="status">
								<span class="visually-hidden"></span>
							</div>
						</div>
					</div>
				</div>
			</div>
			<div style="position: absolute; bottom: 15%;right: 10%;max-width: 300px;">
				<div id="attachtoast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
					<div class="toast-header">
						<strong class="me-auto"></strong>
						<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
					</div>
					<div class="toast-body col-12">
						<button type="button" class="btn btn-sm  btn-primary small"
							onclick="sendTimvideo()">视频电话</button>
						<button type="button" class="btn btn-sm  btn-primary small" onclick="sendfilemod()">传文件</button>
					</div>
				</div>
			</div>
			<div style="position: absolute; bottom: 15%;right: 10%;max-width: 300px;">
				<div id="toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"
					data-bs-autohide="false">
					<div class="toast-header">
						<strong class="me-auto">添加表情</strong>
						<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
					</div>
					<div class="toast-body col-12" id="emoji">
					</div>
				</div>
			</div>

			<div class="chat-footer d-flex align-items-center">
				<div class="flex-grow-1 pe-2">
					<div class="input-group">
						<input type="button" class="input-group-text" onclick="showAttachtoast()" value="&#10133;" />
						<input type="button" class="input-group-text" onclick="showToast()" value="&#128522;" />
						<input type="text" class="form-control" placeholder="输入消息..." id="msgid">
					</div>
				</div>
				<div class="chat-footer-menu1">
					<button type="button" class="btn btn-facebook" onclick="send()">
						<svg class="bi bi-cursor" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor"
							xmlns="http://www.w3.org/2000/svg">
							<path fill-rule="evenodd"
								d="M14.082 2.182a.5.5 0 0 1 .103.557L8.528 15.467a.5.5 0 0 1-.917-.007L5.57 10.694.803 8.652a.5.5 0 0 1-.006-.916l12.728-5.657a.5.5 0 0 1 .556.103zM2.25 8.184l3.897 1.67a.5.5 0 0 1 .262.263l1.67 3.897L12.743 3.52 2.25 8.184z" />
						</svg>
					</button>
				</div>
			</div>
			<!--start chat overlay-->
			<div class="overlay chat-toggle-btn-mobile"></div>
			<!--end chat overlay-->
		</div>
	</div>

	<!-- <div class="overlay toggle-icon"></div>
	<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
	</div> -->
	<script>
		const audio = {
			volume: 1,
			sampleRate: 48000,
			sampleSize: 16,
			echoCancellation: true,
			autoGainControl: true,
			noiseSuppression: true,
			latency: 0.3,
			channelCount: 2,
			deviceId: "",
		}
		const video = {
			facingMode: "user",
			deviceId: "",
			resizeMode: false,
			// width: { min: 50, ideal: 300, max: 500 }, // 想要的理想宽度
			// height: {min: 50, ideal: 300, max: 500 }, // 想要的理想高度
			// frameRate: { ideal: 30 } // 想要的理想帧率
		}
	</script>
	<script>
		new PerfectScrollbar('.chat-list');
		new PerfectScrollbar('.chat-content');
		function showToast() {
			const toast = new bootstrap.Toast(document.getElementById('toast'));
			toast.show();
		}
		function showClose() {
			const toast = new bootstrap.Toast(document.getElementById('toast'));
			toast.hide();
		}
		function showAttachtoast() {
			const toast = new bootstrap.Toast(document.getElementById('attachtoast'));
			toast.show();
			showClose();
		}
		function showsendToast() {
			const toast = new bootstrap.Toast(document.getElementById('sendfiletoast'));
			toast.show();
		}
		function addemoji(t) {
			let v = document.getElementById("msgid");
			v.value = v.value + t.innerHTML;
		}
	</script>

	<script>
		function mutlivideoroomshow() {
			$("#modal-newvideogroup").modal("show");
		}
		function mutlivideoroomclose() {
			$("#modal-newvideogroup").modal("hide");
		}
	</script>

	<script>
		$(function () {
			$("#modal-btn-control").click(function () {
				$("#modal-control").modal("toggle");
			});
		});

		function controlclose() {
			$("#modal-control").modal("hide");
		}

		function technologyshow() {
			$("#modal-technology").modal("show");
		}


		function technologyclose() {
			$("#modal-technology").modal("hide");
		}

		$(function () {
			$("#modal-btn-group").click(function () {
				$("#modal-group").modal("toggle");
				myroom();
			});
		});

		$(function () {
			$("#modal-btn-newgroup").click(function () {
				$("#modal-newgroup").modal("toggle");
			});
		});

		$(function () {
			$("#group-close").click(function () {
				$("#modal-group").modal("hide");
			});
		});

		$(function () {
			$("#group-close-end").click(function () {
				$("#modal-group").modal("hide");
			});
		});


		$(function () {
			$("#modal-btn-timgroup").click(function () {
				$("#modal-timgroup").modal("toggle");
				timroomlist();
			});
		});

		$(function () {
			$("#timgroup-close").click(function () {
				$("#modal-timgroup").modal("toggle");
			});
		});

		$(function () {
			$("#timgroup-close-end").click(function () {
				$("#modal-timgroup").modal("toggle");
			});
		});

		$(function () {
			$("#newgroup-close").click(function () {
				$("#modal-newgroup").modal("hide");
			});
		});

		$(function () {
			$("#modal-btn-register").click(function () {
				$("#modal-register").modal("toggle");
			});
		});

		function modalregister() {
			$("#modal-register").modal("show");
		}

		$(function () {
			$("#register-close").click(function () {
				$("#modal-register").modal("toggle");
			});
		});

		$(function () {
			$("#register-close-end").click(function () {
				$("#modal-register").modal("toggle");
			});
		});

		$(function () {
			$("#modal-btn-webtimuser").click(function () {
				webtimuserlist()
			});
		});

		$(function () {
			$("#friend-close").click(function () {
				$("#modal-webtimuser").modal("hide");
			});
		});

		$(function () {
			$("#friend-close-end").click(function () {
				$("#modal-webtimuser").modal("hide");
			});
		});

		$(function () {
			$("#groupmember-close").click(function () {
				$("#modal-groupmember").modal("hide");
			});
		});

		$(function () {
			$("#groupmember-close-end").click(function () {
				$("#modal-groupmember").modal("hide");
			});
		});


		function modaltimselfshow() {
			$("#modal-timself").modal("show");
			selfdiv();
		}

		function timselfclose() {
			$("#modal-timself").modal("hide");
			if (isEmpty(myAccount)) {
				document.getElementById("timselflist").innerHTML = "<h3>未登录</h3>";
			} else {
				document.getElementById("timselflist").innerHTML = "";
			}
		}

		$(function () {
			$("#modal-btn-timmyfriend").click(function () {
				$("#modal-timmyfriend").modal("show");
				timmyfriendlist();
			});
		});

		function timmyfriendshow() {
			$("#modal-timmyfriend").modal("show");
			timmyfriendlist();
		}

		function timmyfriendclose() {
			$("#modal-timmyfriend").modal("hide");
		}

		function fullscreen(e) {
			if (e.requestFullscreen) {
				e.requestFullscreen();
			} else if (e.mozRequestFullScreen) {
				e.mozRequestFullScreen();
			} else if (e.webkitRequestFullscreen) {
				e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
			}
		}

		function videostreamfullscreen() {
			let e = document.getElementById("videostreamdiv");
			fullscreen(e);
		}

		function livefullscreen() {
			fullscreen(playvideowindow);
		}

	</script>

	<script>
		var mutlivideostreamid = document.getElementById("mutlivideostreamid")
		function modalmutlivideoshow() {
			if (isEmpty(mutlivideostreamid.value)) {
				$("#modal-mutlivideo").modal("show");
				videoroomlist();
			} else {
				mutlivideostreamshow();
			}
		}

		function modalmutlivideoclose() {
			$("#modal-mutlivideo").modal("hide");
		}

		function videoroomlist() {
			let mygrouplist = document.getElementById("mygrouplist_v");
			mygrouplist.innerHTML = "";
			videoroomMap.forEach((v, k) => {
				mygrouplist.innerHTML = mygrouplist.innerHTML + myvideoroomdiv(v.cover, v.name, v.node);
			})
		}

		const pullmemberdiv = (account) => {
			let n = timNodeMap.get(account);
			let cover = "";
			let name = "";
			if (!isEmpty(n)) {
				cover = n.cover;
				name = n.name;
			}
			let s = '<div class="card m-1" style="width:90px;height: 120px;">'
				+ '<img class="card-img-top" src="' + cover + '" alt="Card image" style="width:100%;height: 70px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:90px;font-size:smaller;">' + name + '</h6>'
				+ '<div class="form-check">'
				+ '<label class="form-check-label">选择</label>'
				+ '<input class="form-check-input" type="checkbox" id="pullbox" name="pullbox" value="' + account + '">'
				+ '</div>'
				+ '</div>'
			return s;
		}

		function modalvideopullmembershow(account) {
			$("#modal-videopullmember").modal("show");
			document.getElementById("videopullmemberAccount").value = account;
			let modvideopullmemberlist = document.getElementById("modvideopullmemberlist");
			modvideopullmemberlist.innerHTML = "";
			let alist = roomMemberMap.get(account);
			let alm = new Map();
			if (!isEmpty(alist)) {
				alist.forEach((v) => {
					alm.set(v, 1)
				})
			}
			friendMap.forEach((v, k) => {
				if (!alm.has(k)) {
					modvideopullmemberlist.innerHTML = modvideopullmemberlist.innerHTML + pullmemberdiv(k);
				}
			})
			if (isEmpty(modvideopullmemberlist.innerHTML)) {
				modvideopullmemberlist.innerHTML = "<h4>所有好友均已是该群成员</h4>"
			}
		}

		function modalvideopullmemberclose() {
			$("#modal-videopullmember").modal("hide");
			document.getElementById("modvideopullmemberlist").innerHTML = "";
		}

		const pullmember = (account) => {
			modalvideopullmembershow(account);
		}

		const submitpullmember = () => {
			let pullboxs = document.getElementsByName("pullbox");
			let rnode = document.getElementById("videopullmemberAccount").value
			pullboxs.forEach((b) => {
				if (b.checked) {
					tc.PullInRoom(rnode, b.value);
				}
			})
			modalvideopullmemberclose();
		}

		const mutlivideostreamshow = () => {
			$("#modal-mutlivideostream").modal("show");
		}

		const mutlivideostreamhide = () => {
			$("#modal-mutlivideostream").modal("hide");
		}

		const mutlivideostreamclose = () => {
			if (confirm("确定退出实时群聊天？")) {
				mulMediaroommap.forEach((v, k) => {
					tc.VirtualroomSubCancel(k)
				})
				mulMediaroommap.clear();
				liveroommediaStop();
				mutlivideostreamlist.innerHTML = "";
				$("#modal-mutlivideostream").modal("hide");
			}
		}

		const roomvideoopen = (account) => {
			if (!confirm("🎥开始多人实时视频?")) {
				return
			}
			roomvideostart(account)
		}

		let mutlivideostreamlist = document.getElementById("mutlivideostreamlist")

		const roomvideostart = (account) => {
			if (isEmpty(mutlivideostreamid.value)) {
				document.getElementById("userinfo_token").value = webtimToken;
				let vdata = $("#userinfo_form").serialize();
				httpPost("vregister", vdata, (data) => {
					if (!isEmpty(data)) {
						if (data == "0") {
							alert("视频启动失败")
						} else {
							let jsonres = JSON.parse(data);
							if (jsonres.ok) {
								webtimVnode = jsonres.n;
								document.getElementById("videoroomname").innerText = roomMap.get(account).name;
								let vs = createVideoroomDiv("我")
								let video = document.createElement("video");
								vs.appendChild(video)
								video.style = "max-width:250px;max-height: 250px;"
								video.setAttribute('muted', '');
								video.autoplay = true;
								if (mutlivideostreamlist.children.length > 0) {
									let firstdiv = mutlivideostreamlist.querySelector(':scope > div:first-child')
									if (firstdiv) {
										mutlivideostreamlist.replaceChild(vs, firstdiv)
									}
								} else {
									mutlivideostreamlist.appendChild(vs);
								}
								mutlivideostreamid.value = account;
								liveroommediaStart(video);
								return
							}
							alert("视频启动失败");
						}
					}
				});
			}
		}

		var liveroomvideo = null;
		async function liveroommediaStart(videoliveObject) {
			try {
				videoliveObject.innerHTML = '<source src="video.mp4" type="video/mp4"><source src="video.webm" type="video/webm">'
				liveroomvideo = new MediaRTC(videoliveObject, { video: video, audio: audio });
				// liveroomvideo.recorderHz = 600;
				// liveroomvideo.audioBitsPerSecond = 320000;
				// liveroomvideo.videoBitsPerSecond = 100000;
				await liveroomvideo.MediaDevices();
				liveroomvideo.sendStream = pushLiveVideoStream
				liveroomvideo.Pushstream();
				modalmutlivideoclose()
				mutlivideostreamshow();
				let account = mutlivideostreamid.value
				tc.StreamToRoom(account, webtimVnode, 0, 21);
				notifyroom.send(account, webtimVnode)
			} catch (err) {
				mulMediaroommap.forEach((v, k) => {
					tc.VirtualroomSubCancel(k)
				})
				mulMediaroommap.clear();
				liveroommediaStop();
				mutlivideostreamlist.innerHTML = "";
				$("#modal-mutlivideostream").modal("hide");
				alert("未获得麦克风或摄像头权限")
			}
		}

		function liveroommediaStop() {
			if (!isEmpty(liveroomvideo)) {
				liveroomvideo.MediaRecorderStop();
			}
			if (!isEmpty(liveroomAudio)) {
				liveroomAudio.MediaRecorderStop();
			}
			liveroomvideo = null;
			liveroomAudio = null;
			webtimVnode = null;
			mutlivideostreamid.value = "";

			notifyroom.stop()
		}

		function pushLiveVideoStream(buffer) {
			if (!isEmpty(webtimVnode)) {
				sendLiveStream(buffer, webtimVnode)
			}
		}

		const roomAudioOpen = (account) => {
			if (!confirm("📞开始多人实时语音?")) {
				return
			}
			roomAudiostart(account)
		}
		async function roomAudiostart(account) {
			if (isEmpty(mutlivideostreamid.value)) {
				document.getElementById("userinfo_token").value = webtimToken;
				let vdata = $("#userinfo_form").serialize();
				httpPost("vregister", vdata, (data) => {
					if (!isEmpty(data)) {
						if (data == "0") {
							alert("语音启动失败")
						} else {
							let jsonres = JSON.parse(data);
							if (jsonres.ok) {
								webtimVnode = jsonres.n;
								document.getElementById("videoroomname").innerText = roomMap.get(account).name;
								let sc = createImgroomDiv("我", timNodeMap.get(myAccount).cover)
								if (mutlivideostreamlist.children.length > 0) {
									let firstdiv = mutlivideostreamlist.querySelector(':scope > div:first-child')
									if (firstdiv) {
										mutlivideostreamlist.replaceChild(sc, firstdiv)
									}
								} else {
									mutlivideostreamlist.appendChild(sc)
								}
								mutlivideostreamid.value = account;
								liveroomAudioStart();
								return
							}
							alert("语音启动失败");
						}
					}
				});
			}
		}

		var liveroomAudio = null;
		async function liveroomAudioStart() {
			liveroomAudio = new AudioRTC({ audio: audio });
			liveroomAudio.recorderHz = 600;
			try {
				await liveroomAudio.MediaDevices()
				liveroomAudio.sendStream = pushLiveVideoStream
				liveroomAudio.Pushstream(true);
				modalmutlivideoclose()
				mutlivideostreamshow();
				let account = mutlivideostreamid.value
				tc.StreamToRoom(account, webtimVnode, 0, 31);
				notifyroom.send(account, webtimVnode)
			} catch (err) {
				mulMediaroommap.forEach((v, k) => {
					tc.VirtualroomSubCancel(k)
				})
				mulMediaroommap.clear();
				liveroommediaStop();
				mutlivideostreamlist.innerHTML = "";
				$("#modal-mutlivideostream").modal("hide");
				alert("未获得麦克风权限");
			}
		}

		const switchovervideo = () => {
			if (!isEmpty(liveroomAudio)) {
				let account = mutlivideostreamid.value;
				mutlivideostreamid.value = "";
				let vnode = webtimVnode
				liveroommediaStop()
				tc.StreamToRoom(account, vnode, 0, 33);
				roomvideostart(account)
			}
		}

		const switchoveraudio = () => {
			if (!isEmpty(liveroomvideo)) {
				let account = mutlivideostreamid.value;
				mutlivideostreamid.value = "";
				let vnode = webtimVnode
				liveroommediaStop()
				tc.StreamToRoom(account, vnode, 0, 33);
				roomAudiostart(account)
			}
		}



	</script>
	<script>
		const livelistDiv = (cover, topic, nick, vnode, ltype, node) => {
			let open = '<button type="button" class="btn btn-sm btn-primary" onclick=openlivevideo("' + vnode + '","' + node + '","' + topic + '")>打开</button>';
			if (ltype == 2) {
				open = '<button type="button" class="btn btn-sm btn-danger" onclick=openlivevideo("' + vnode + '","' + node + '","' + topic + '")>打开</button>';
			}
			let s = '<div class="card m-1" style="width:200px;height: 240px;">'
				+ '<img class="card-img-top" src="' + cover + '" alt="Card image" style="width:100%;height: 150px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:180px;"><B>' + topic + '</B></h6>'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:180px;font-size:15px;">[直播者:' + nick + ']</h6>'
				+ '<div class="btn-group">'
				+ open
				+ '</div>'
				+ '</div>'
			return s;
		}

		function timlivelist() {
			document.getElementById("modalvideolivelist").innerHTML = "";
			httpPost("livelist", null, (data) => {
				if (!isEmpty(data)) {
					let modalvideolivelist = document.getElementById("modalvideolivelist");
					let jsonres = JSON.parse(data);
					let items = [];
					for (let i = jsonres.length - 1; i >= 0; i--) {
						items.push(livelistDiv(jsonres[i].Cover, replace(jsonres[i].Topic), replace(jsonres[i].Nick), jsonres[i].Vnode, jsonres[i].LiveType, jsonres[i].Node));
					}
					modalvideolivelist.innerHTML = items.join("");
				}
			});
		}

	</script>
	<script>
		var myAccount = "";
		var initcount = 1;
		function modalLogin() {
			try {
				if (isEmpty(myAccount)) {
					$("#modal-login").modal({ backdrop: 'static', keyboard: false });
					$("#modal-login").modal("show");
				}
			} catch (err) {
				sleep(5).then(() => modalLogin())
			}
		}

		$("#modal-login").on('hide.bs.modal', function () {
			sleep(500).then(() => modalLogin())
		});


		function timvideoshow() {
			$("#modal-timvideo").modal("show");
		}

		function timvideoclose() {
			if (!isEmpty(videoPhoneAccount)) {
				if (!confirm("确定结束视频电话?")) {
					return
				}
			}
			$("#modal-timvideo").modal("hide");
			videophoneStop();
		}

		$(function () {
			$("#modal-btn-timlivestream").click(function () {
				$("#modal-timlivestream").modal("show");
				timlivelist();
			});
		});
		$(function () {
			$("#modal-btn-timliveroomstream").click(function () {
				modalmutlivideoshow();
			});
		});

		function flushnewlive() {
			timlivelist();
		}


		function timlivestreamclose() {
			$("#modal-timlivestream").modal("hide");
		}

		let vjoin4videostream = null;
		let subVnode = null;
		function openlivevideo(vnode, node, topic) {
			if (isEmpty(myAccount)) {
				alert("未登录");
				return
			}
			$("#modal-videostream").modal({ backdrop: 'static', keyboard: false });
			$("#modal-videostream").modal("show");
			vjoin4videostream = new VideoJoin(document.getElementById("videostreamdiv"));
			vjoin4videostream.modeRealtime = false;
			vjoin4videostream.modeSpeedRateup = true;
			newlivecheck(vnode, node)
			document.getElementById("videostreamtitle").innerText = topic;
		}

		function videostreamclose() {
			$("#modal-videostream").modal("hide");
			if (!isEmpty(myAccount)) {
				tc.VirtualroomSubCancel(subVnode)
				subVnode = null;
				vjoin4videostream.Clear();
				document.getElementById("videostreamdiv").innerHTML = "";
			}
		}

	</script>
	<script>
		const accountdiv = (cover, name, account, active) => {
			let n = timNodeMap.get(account);
			let addbut = "";
			let owner = '';
			let friend = '';
			let activestat = "&#127774;";
			if (!active) {
				activestat = "";
			}
			if (account != myAccount && isEmpty(friendMap.get(account))) {
				addbut = '<button type="button" class="btn btn-sm btn-primary" onclick=addfriend("' + account + '","' + name + '")>加好友</button>';
			}
			if (account == myAccount) {
				owner = '<button type="button" class="btn btn-sm  btn-info">我</button>'
			}
			if (!isEmpty(friendMap.get(account)) && account != myAccount) {
				friend = '<button type="button" class="btn btn-sm  btn-warning">好友</button>'
			}
			let s = '<div class="card m-1" style="width:120px;height: 170px;">'
				+ '<img class="card-img-top" src="' + cover + '" alt="Card image" style="width:100%;height: 100px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:120px;">' + activestat + name + '</h6>'
				+ '<div class="btn-group">'
				+ addbut + owner + friend
				+ '</div>'
				+ '</div>'
			return s;
		}

		const gmemeberdiv = (node, fnode, rnode) => {
			let n = timNodeMap.get(node);
			let addbut = "";
			let kickbut = "";
			let roomowner = '';
			let owner = '';
			let friend = '';
			if (node != myAccount && isEmpty(friendMap.get(node))) {
				addbut = '<button type="button" class="btn btn-sm  btn-primary" onclick=addfriend("' + node + '","' + n.name + '")>加好友</button>';
			}
			if (node != myAccount && fnode == myAccount) {
				kickbut = '<button type="button" class="btn btn-sm  btn-danger" onclick=kickroom("' + node + '","' + n.name + '","' + rnode + '")>踢出群</button>';
			}
			if (fnode == node) {
				roomowner = '<button type="button" class="btn btn-sm  btn-success">群主</button>';
			}
			if (node == myAccount) {
				owner = '<button type="button" class="btn btn-sm  btn-info">我</button>'
			}
			if (!isEmpty(friendMap.get(node)) && node != myAccount) {
				friend = '<button type="button" class="btn btn-sm  btn-warning">好友</button>'
			}
			let s = '<div class="card m-1" style="width:160px;height: 200px;">'
				+ '<img class="card-img-top" src="' + n.cover + '" alt="Card image" style="width:100%;height: 130px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:120px;">' + n.name + '</h6>'
				+ '<div class="btn-group">'
				+ kickbut + addbut + roomowner + owner + friend
				+ '</div>'
				+ '</div>'
			return s;
		}

		const myroomdiv = (cover, topic, account) => {
			let n = roomMap.get(account);
			let mynode = "";
			if (n.fnode == myAccount) {
				mynode = "&#128308;";
			}
			let s = '<div class="card m-1" style="width:130px;height: 150px;">'
				+ '<img class="card-img-top m-1" src="' + cover + '" alt="Card image" style="width:100%;height: 100px;" />'
				// + '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:120px;">' + mynode + topic + '</h6>'
				+ '<button type="button" class="btn btn-primary btn-sm" onclick=groupmember("' + account + '") style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:110px;">' + mynode + topic + '</button>'
				+ '</div>'
			return s;
		}

		const myvideoroomdiv = (cover, topic, account) => {
			let n = videoroomMap.get(account);
			let mynode = "";

			let memberbtn = '<button type="button" class="btn btn-primary btn-sm" onclick=groupmember("' + account + '")>成员</button>'
			let pullmemberbtn = '<button type="button" class="btn btn-danger btn-sm" onclick=pullmember("' + account + '")>添加</button>'
			let audiobtn = '<button type="button" class="btn btn-success btn-sm" onclick=roomAudioOpen("' + account + '")>语音</button>'
			let videobtn = '<button type="button" class="btn btn-info btn-sm" onclick=roomvideoopen("' + account + '")>视频</button>'

			if (n.fnode == myAccount) {
				mynode = "&#128308;";
			} else {
				pullmemberbtn = ""
			}
			let s = '<div class="card m-1" style="width:220px;height: 200px;">'
				+ '<img class="card-img-top" src="' + cover + '" alt="Card image" style="width:100%;height: 140px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:200px;">' + mynode + topic + '</h6>'
				+ '<div class="btn-group">'
				+ memberbtn + pullmemberbtn + audiobtn + videobtn
				+ '</div>'
				+ '</div>'
			return s;
		}

		const mytimfrienddiv = (account) => {
			let n = timNodeMap.get(account);
			let cover = "";
			let name = "";
			if (!isEmpty(n)) {
				cover = n.cover;
				name = n.name;
			}
			let s = '<div class="card m-1" style="width:180px;height: 200px;">'
				+ '<img class="card-img-top" src="' + cover + '" alt="Card image" style="width:100%;height: 140px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:120px;">' + name + '</h6>'
				+ '<div class="btn-group">'
				+ '<button type="button" class="btn btn-sm  btn-primary small" onclick=sendmsg("' + account + '")>聊天</button>'
				+ '<button type="button" class="btn btn-sm  btn-primary small" onclick=deleteroster("' + account + '")>删除</button>'
				+ '</div>'
				+ '</div>'
			return s;
		}

		const timroomdiv = (cover, topic, account, gtype) => {
			let but = '';
			if (gtype == 2) {
				but = '<button type="button" class="btn btn-success btn-sm" onclick=addroom("' + account + '")>加入群</button>'
			} else {
				but = '<button type="button" class="btn btn-danger btn-sm" onclick=addroom("' + account + '")>加入群</button>'
			}
			let s = '<div class="card m-1" style="width:100px;height: 140px;">'
				+ '<img class="card-img-top" src="' + cover + '" alt="Card image" style="width:100%;height: 80px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:100px;">' + topic + '</h6>'
				+ but + '</div>'
			return s;
		}

		const selfdiv = () => {
			if (isEmpty(myAccount)) {
				return
			}
			let node = timNodeMap.get(myAccount);
			if (isEmpty(node)) {
				return
			}
			let timselflist = document.getElementById("timselflist");
			let buts = '<div class="btn-group">'
				+ '<button type="button" class="btn btn-primary" onclick="openmodifyPwdcard()">修改密码</button>&nbsp;'
				+ '<button type="button" class="btn btn-primary" onclick="openmodifyUserinfocard()">修改资料</button>'
				+ '</div>'

			let s = '<div class="card m-1" style="width:250px;height: 300px;">'
				+ '<img class="card-img-top" src="' + node.cover + '" alt="Card image" style="width:100%;height: 230px;" />'
				+ '<h6 class="card-title" style="text-overflow:ellipsis; white-space:nowrap; overflow:hidden;width:230px;">' + node.name + '</h6>'
				+ buts + '</div>'
			timselflist.innerHTML = s;
		}

		function openmodifyPwdcard() {
			unmodifyUserinfo();
			if (document.getElementById("modifyPwdcard").style.display != "inline") {
				document.getElementById("modifyPwdcard").style.display = "inline";
			} else {
				document.getElementById("modifyPwdcard").style.display = "none";
			}
		}

		function openmodifyUserinfocard() {
			unmodifyPwd();
			if (document.getElementById("modifyUserinfocard").style.display != "inline") {
				document.getElementById("modifynick").value = timNodeMap.get(myAccount).name;
				document.getElementById("modifyUserinfocard").style.display = "inline";
				document.getElementById("headcover").src = timNodeMap.get(myAccount).cover;
				document.getElementById("headcover2").value = timNodeMap.get(myAccount).cover;
			} else {
				document.getElementById("modifyUserinfocard").style.display = "none";
			}
		}
		function unmodifyPwd() {
			document.getElementById("oldpassword").value = "";
			document.getElementById("newpassword").value = "";
			document.getElementById("modifyPwdcard").style.display = "none";
		}
		function unmodifyUserinfo() {
			document.getElementById("modifyUserinfocard").style.display = "none";
		}

		function modifyPwd() {
			let oldpassword = document.getElementById("oldpassword").value;
			let newpassword = document.getElementById("newpassword").value;
			if (oldpassword == newpassword) {
				alert("密码没有变化");
				return
			}
			tc.Modify(oldpassword, newpassword);
		}


		function modifyUserinfo() {
			const coverfile = document.getElementById("coverinputs")
			if (coverfile.files.length > 0) {
				const file = coverfile.files[0]
				const formDate = new FormData()
				formDate.append("coverfile", file)
				httpPostBinary("upcover", formDate, (data) => {
					if (data != "0") {
						modifyUserinfo2(data)
					} else {
						alert("修改资料失败");
					}
				})
			} else {
				modifyUserinfo2(null)
			}
		}

		function modifyUserinfo2(cover) {
			document.getElementById("modifytoken").value = webtimToken;
			let modifynick = document.getElementById("modifynick");
			if (modifynick.value.length > 16) {
				alert("昵称长度超过限制");
				return
			}
			if (modifynick.value.length == 0) {
				alert("昵称为空");
				return
			}
			if (!isEmpty(cover)) {
				document.getElementById("headcover2").value = cover;
			}
			let data = $("#modifyuserinfoform").serialize();
			httpPost("modifyuserinfo", data, (data) => {
				try {
					if (!isEmpty(data)) {
						let jsonres = JSON.parse(data);
						if (jsonres.ok) {
							tc.UserInfo([myAccount])
							unmodifyUserinfo();
							alert("修改资料成功");
							sleep(1000).then(() => selfdiv());
							tc.BroadPresence(3, 0, null);
						} else {
							alert("修改资料失败");
						}
					}
				} catch (err) {
					alert("修改资料失败");
				}
			});
		}


		function register() {
			if (document.getElementById("registerNick").value.length > 16) {
				alert("昵称长度超过限制");
				return
			}
			if (document.getElementById("registername").value.length == 0 ||
				document.getElementById("registerpwd").value.length == 0 ||
				document.getElementById("registerNick").value.length == 0
			) {
				alert("注册信息不完整")
				return
			}
			let fdata = $("#registerform").serialize();
			httpPost("register", fdata, (data) => {
				if (!isEmpty(data)) {
					let jsonres = JSON.parse(data);
					if (jsonres.ok) {
						alert("注册成功");
						$("#modal-register").modal("hide");
					} else {
						alert("注册失败:" + jsonres.error);
					}
				}
			});
		}

		async function webtimuserlist() {
			$("#modal-webtimuser").modal("show");

			httpPost("userslist", null, (data) => {
				if (!isEmpty(data)) {
					let accountlist = document.getElementById("accountlist");
					let jsonres = JSON.parse(data);
					let items = [];
					for (let i = 0; i < jsonres.length; i++) {
						items.push(accountdiv(jsonres[i].Cover, replace(jsonres[i].Nickname), jsonres[i].Account, jsonres[i].Active));
					}
					accountlist.innerHTML = items.join("");
				}
			});
		}

		function timroomlist() {
			httpPost("roomlist", null, (data) => {
				if (!isEmpty(data)) {
					let timgrouplist = document.getElementById("timgrouplist");
					let jsonres = JSON.parse(data);
					let items = [];
					for (let i = jsonres.length - 1; i >= 0; i--) {
						items.push(timroomdiv(jsonres[i].Cover, replace(jsonres[i].Topic), jsonres[i].Account, jsonres[i].Gtype));
					}
					timgrouplist.innerHTML = items.join("");
				}
			});
		}

		function timmyfriendlist() {
			let modalmyfriendlist = document.getElementById("modalmyfriendlist");
			modalmyfriendlist.innerHTML = "";
			for (const node of friendMap.keys()) {
				modalmyfriendlist.innerHTML = modalmyfriendlist.innerHTML + mytimfrienddiv(node)
			}
		}

		function groupmember(node) {
			let nodes = roomMemberMap.get(node);
			let n = roomMap.get(node);
			let groupmemberlist = document.getElementById("groupmemberlist");
			document.getElementById("groupmember_node").value = node;
			groupmemberlist.innerHTML = "";
			if (!isEmpty(nodes)) {
				let arrs = [];
				nodes.forEach((unode) => {
					arrs.push(gmemeberdiv(unode, n.fnode, node));
				})
				groupmemberlist.innerHTML = arrs.join("");
			}
			document.getElementById("roomid").innerHTML = "<h5>&#128246;" + n.name + "</h5>"
			$("#modal-groupmember").modal("show");
		}

		function myroom() {
			let mygrouplist = document.getElementById("mygrouplist");
			let m = []
			roomMap.forEach((v, k) => {
				m.push(myroomdiv(v.cover, v.name, k));
			})
			mygrouplist.innerHTML = m.join("");
		}

		function addfriend(account, name) {
			if (isEmpty(myAccount)) {
				alert("未登录");
				return
			}
			if (account == myAccount) {
				alert("不能加自己为好友");
				return
			}
			if (!isEmpty(friendMap.get(account))) {
				alert("已经是好友");
				return
			}
			if (confirm("确定向[" + name + "]发送加好友请求?")) {
				tc.Addroster(account, "我是" + timNodeMap.get(myAccount).name);
				alert("已成功向[" + name + "]发送加好友信息");
			}
		}

		function newgroup() {
			if (isEmpty(myAccount)) {
				alert("未登录");
				return
			}
			if (isEmpty(document.getElementById("grouptopic").value)) {
				alert("请填写群主题")
				return
			}
			document.getElementById("newgrouptoken").value = webtimToken;
			let vdata = $("#newgroupform").serialize();
			httpPost("newroom", vdata, (data) => {
				if (!isEmpty(data)) {
					let mygrouplist = document.getElementById("mygrouplist");
					let jsonres = JSON.parse(data);
					roomMap.set(jsonres.Node, new timNode(jsonres.Node, jsonres.Topic, jsonres.Cover, myAccount));
					mygrouplist.innerHTML = mygrouplist.innerHTML + myroomdiv(jsonres.Cover, jsonres.Topic, jsonres.Node);
					// tc.UserRoom();
					tc.RoomInfo([jsonres.Node]);
					tc.RoomUsers(jsonres.Node);
					$("#modal-newgroup").modal("hide");
				} else {
					alert("建群失败");
				}
			});
		}

		function newvideogroup() {
			if (isEmpty(myAccount)) {
				alert("未登录");
				return
			}
			if (isEmpty(document.getElementById("grouptopic_v").value)) {
				alert("请填写群主题")
				return
			}
			document.getElementById("newgrouptoken_v").value = webtimToken;
			let vdata = $("#newgroupform_v").serialize();
			httpPost("newroom", vdata, (data) => {
				if (!isEmpty(data)) {
					let mygrouplist = document.getElementById("mygrouplist_v");
					let jsonres = JSON.parse(data);
					videoroomMap.set(jsonres.Node, new timNode(jsonres.Node, jsonres.Topic, jsonres.Cover, myAccount));
					mygrouplist.innerHTML = mygrouplist.innerHTML + myvideoroomdiv(jsonres.Cover, jsonres.Topic, jsonres.Node);
					tc.RoomInfo([jsonres.Node]);
					tc.RoomUsers(jsonres.Node);
					mutlivideoroomclose();
				} else {
					alert("建群失败");
				}
			});
		}

		function addroom(account) {
			if (isEmpty(myAccount)) {
				alert("未登录");
				return
			}
			if (confirm("确定发送入群申请?")) {
				if (!isEmpty(roomMap.get(account))) {
					alert("你已经是该群成员");
					return
				}
				tc.AddRoom(account, "我是" + timNodeMap.get(myAccount).name);
				alert("已经成功发送入群申请");
			}
		}

		function leaveroom() {
			let groupmembernode = document.getElementById("groupmember_node").value;
			let n = roomMap.get(groupmembernode);
			if (n.fnode == myAccount) {
				alert("群主不能退群");
				return
			}
			if (confirm("确定退出该群?")) {
				tc.StreamToRoom(groupmembernode, null, 2, 0);
				tc.LeaveRoom(groupmembernode);
				$("#modal-groupmember").modal("hide");
			}
		}

		function kickroom(unode, name, rnode) {
			if (confirm("确定将[" + name + "]移除出群?")) {
				tc.StreamToRoom(rnode, rnode, 3, 0);
				tc.KickRoom(rnode, unode);
				let nlist = roomMemberMap.get(rnode);
				let t = [];
				for (const n of nlist) {
					if (n != unode) {
						t.push(n);
					}
				}
				roomMemberMap.set(rnode, t);
			}
		}

		function sendmsg(account) {
			timmyfriendclose()
			peer(account, false);
		}

		function deleteroster(account) {
			if (confirm("确定删除好友[" + timNodeMap.get(account).name + "]")) {
				tc.Rmroster(account)
				sleep(2000).then(() => timmyfriendlist());
			}
		}

		function sendTimvideo(account) {
			if (isEmpty(account)) {
				account = accountId.value
				if (isEmpty(account)) {
					alert("未选择好友")
					return
				}
				if (!friendMap.has(account)) {
					alert("只能与好友视频")
					return
				}
			}
			if (videoPhoneAccount != "") {
				timvideoshow();
			} else {
				if (confirm("确定向好友[" + timNodeMap.get(account).name + "]发送视频请求？")) {
					document.getElementById("readylink").innerHTML = "<h2>连线中...<h2><h2></h2>"
					rockonTime(account, 30);
					timvideoshow();
					videoPhoneAccount = account;
					videophoneStart();
					tc.StreamToUser(account, null, 0, 1);
				}
			}
		}

		function rockonTime(account, count) {
			if (count < 0) {
				if (videoPhoneAccount == account) {
					videoPhoneAccount = ""
					document.getElementById("readylink").innerHTML = "超时无人接听";
				}
				return
			}
			let t = document.getElementById("readylink").children[1];
			if (!isEmpty(t)) {
				t.innerText = count + "秒"
				sleep(1000).then(() => rockonTime(account, count - 1))
			}
		}
	</script>
	<script>
		var MB = 1000000;
		var KB = 1000;
		var HKB = 100 * KB;
		function sendfileshow() {
			$("#modal-sendfile").modal("show");
		}
		function sendfileclose() {
			$("#modal-sendfile").modal("hide");
		}
		function sendfilemod(account) {
			if (isEmpty(account)) {
				account = accountId.value
				if (isEmpty(account)) {
					alert("未选择好友")
					return
				}
				if (!friendMap.has(account)) {
					alert("只能向好友传文件")
					return
				}
			}
			sendfileshow();
			document.getElementById("recvnode").value = account
			document.getElementById("sendfilehead").src = timNodeMap.get(account).cover;
			document.getElementById("transfileId").value = "";
		}

		function sendfile() {
			let recvnode = document.getElementById("recvnode").value
			let files = document.getElementById("transfileId").files
			if (files.length == 1) {
				document.getElementById("userinfo_token").value = webtimToken;
				let vdata = $("#userinfo_form").serialize();
				httpPost("vregister", vdata, (data) => {
					if (!isEmpty(data)) {
						if (data == "0") {
							alert("发送失败")
						} else {
							let jsonres = JSON.parse(data);
							if (jsonres.ok) {
								sendfileMap.set(jsonres.n, new sendFileBean(files[0], jsonres.n, recvnode))
								let s = { "name": files[0].name, "size": files[0].size, "vnode": jsonres.n }
								tc.StreamToUser(recvnode, JSON.stringify(s), 0, 11);
								showsendToast();
								document.getElementById("sendfileshow").innerHTML = "<h6>等待对方接收中...</h6>"
								sendfileclose();
							} else {
								alert("发送失败，请重试");
							}
						}
					}
				});
			} else {
				alert("请选择传输文件")
				return
			}
		}

		function sendfilestart(vnode) {
			let sfb = sendfileMap.get(vnode)
			readfile(sfb.file, (data) => {
				let size = data.length;
				let a = Math.floor(size / HKB);
				showsendToast()
				let t = document.getElementById("sendfileshow");
				let rateid = document.getElementById("rateid");
				let bt = '<button class="btn btn-light" onclick="cancelsendfile(\'' + vnode + '\',true)">取消发送</button>'
				if (a > 0) {
					rateid.innerHTML = bt
					let i = 0
					sendfileLoop(vnode, data, 0, a, (d) => {
						i = i + d
						let r = i / size
						if (r > 1) {
							r = 1
						}
						t.innerHTML = "<h6 style='font-size:smaller;'>" + sfb.file.name + "已经发送:" + Math.floor(100 * r) + "%</h6>";
					})
				} else {
					tc.PushBigDataStream(vnode, gzip(data))
					sleep(100).then(() => tc.PushBigDataStream(vnode, new Uint8Array([0])));
					t.innerHTML = "<h6 style='font-size:smaller;'>" + sfb.file.name + "已经发送:100%</h6>";
					sendfileMap.delete(vnode)
				}
			});
		}

		function sendfileLoop(vnode, data, stat, count, showfunc) {
			if (!sendfileMap.has(vnode)) {
				return
			}
			tc.PushBigDataStream(vnode, gzip(data.slice(stat * HKB, (stat + 1) * HKB)))
			showfunc(HKB)
			if (stat < count) {
				sleep(100).then(() => sendfileLoop(vnode, data, stat + 1, count, showfunc))
			} else {
				sleep(100).then(() => tc.PushBigDataStream(vnode, new Uint8Array([0])))
				sendfileMap.delete(vnode)
			}
		}

		function finishSendfile() {
			document.getElementById("vnode").value = "";
			document.getElementById("recvnode").value = "";
		}

		function cancelsendfile(vnode, initiative) {
			if (sendfileMap.has(vnode)) {
				let n = sendfileMap.get(vnode)
				if (initiative) {
					tc.StreamToUser(n.tnode, vnode, 0, 14);
				}
				sendfileMap.delete(vnode)
				document.getElementById("sendfileshow").innerHTML = ""
				document.getElementById("rateid").innerHTML = ""
			}
		}
	</script>

	<script>
		function opennewlive() {
			if (isEmpty(myAccount)) {
				alert("未登录");
				return
			}
			$("#modal-newlive").modal({ backdrop: 'static', keyboard: false });
			$("#modal-newlive").modal("show");
		}
		function closenewlive() {
			$("#modal-newlive").modal("hide");
			liveclose();
		}

		function selectfile(t) {
			if (t.value == 2) {
				document.getElementById("videofilediv").removeAttribute("hidden");
				document.getElementById("videofilediv").style.display = "inline";
			} else {
				document.getElementById("videofilediv").style.display = "none";
			}
		}

		function selectvideofile() {
			try {
				document.getElementById("livestreamform_topic").value = document.getElementById("videofile").files[0].name;
			} catch (err) { }
		}

		function newlivestart() {
			let title = document.getElementById("livestreamform_topic").value;
			if (isEmpty(title)) {
				alert("请填写直播主题");
				return;
			}
			if (document.getElementById("livetypefile").checked) {
				let file = document.getElementById("videofile");
				if (file.files.length > 0) {
					videoPlayStart(file.files[0]);
				} else {
					alert("请选择视频文件")
					return
				}
			} else {
				if (isEmpty(webtimVnode)) {
					liveMediaStart();
				} else {
					alert("请先结束其他实时对话")
					return
				}
			}
			document.getElementById("videowindow1").style.display = "none";
			document.getElementById("videowindow2").style.display = "inline";
		}

		let webtimVnode = null;

		function livestart(playObject) {
			let cover = screenshot(playObject);
			if (cover.length < 10) {
				cover = timNodeMap.get(myAccount).cover
			}
			document.getElementById("livestreamform_cover").value = cover;
			document.getElementById("livestreamform_token").value = webtimToken;
			document.getElementById("livestreamform_opera").value = "1";
			let vdata = $("#newliveform").serialize();
			httpPost("vroom", vdata, (data) => {
				if (!isEmpty(data)) {
					if (data == "0") {
						alert("直播失败，请重试")
					} else {
						let jsonres = JSON.parse(data);
						if (jsonres.ok) {
							webtimVnode = jsonres.n;
						} else {
							alert("直播失败，请重试");
						}
					}
				}
			});
		}

		function newlivestop() {
			playvideowindow.innerHTML = "";
			let vnode = webtimVnode
			sleep(500).then(() => tc.PushBigDataStream(vnode, (new Uint8Array([0]))))
			webtimVnode = null;
			document.getElementById("livestreamform_opera").value = "2";
			let vdata = $("#newliveform").serialize();
			sleep(1000).then(() => httpPost("vroom", vdata))
		}

		function newlivecheck(vnode, node) {
			document.getElementById("livestreamform_opera").value = "3";
			document.getElementById("livestreamform_node").value = node;
			document.getElementById("livestreamform_vnode").value = vnode;
			let vdata = $("#newliveform").serialize();
			httpPost("vroom", vdata, (data) => {
				if (!isEmpty(data)) {
					if (data != "1") {
						endvideostreamdivshow()
					} else {
						tc.VirtualroomSubBinary(vnode)
						bigDataStreamEvent.set(vnode, 1)
					}
				}
			});
		}

		function endvideostreamdivshow() {
			document.getElementById("videostreamdiv").innerHTML = "<h3>直播已经结束</h3>"
		}

		let videoplayer = null;
		let playvideowindow = document.getElementById("playvideowindow");
		async function videoPlayStart(file) {
			let videoliveObject = document.createElement("video");
			let videoliveObject2 = document.createElement("video");
			videoliveObject2.controls = true;
			videoliveObject2.autoplay = true;
			videoliveObject2.innerHTML = '<source src="video.mp4" type="video/mp4"><source src="video.webm" type="video/webm">'
			loadvideoFile(videoliveObject2, file, async (v) => {
				videoplayerLoad(v)
			})
		}

		function videoplayerLoad(newv) {
			playvideowindow.appendChild(newv);
			let init = false;
			try {
				videoplayer = new VideoPlayerRTC(newv);
				// videoplayer.recorderHz = 500;
				// videoplayer.videoBitsPerSecond = 100000;
			} catch (err) {
				alert("can't playing local files");
			}
			try {
				if (document.getElementById("fileloop").checked) {
					newv.loop = true;
				}
				newv.addEventListener("canplaythrough", (e) => {
					if (!init) {
						videoplayer.Play();
						videoplayer.sendStream = pushVideoplayerStream
						init = true
						livestart(newv)
						videoplayer.Pushstream(true)
					}
				})
				newv.addEventListener("ended", (e) => {
					videoplayerStop();
					playvideowindow.innerHTML = "<h4>视频播放已经结束</h4>";
				})
			} catch (err) {
				console.error(err)
			}
		}

		function videoplayerStop() {
			videoplayer.MediaRecorderStop();
			newlivestop();
		}


		function pushVideoplayerStream(buffer) {
			if (!isEmpty(webtimVnode)) {
				sendLiveStream(buffer, webtimVnode)
			}
		}

		const sendLiveStream = (buffer, vnode) => {
			tc.PushBigDataStream(vnode, gzip((buffer)))
		}

		function livestop() {
			if (!confirm("确定结束直播？")) {
				return
			}
			liveclose();
		}

		function liveclose() {
			document.getElementById("videowindow1").style.display = "inline";
			document.getElementById("videowindow2").style.display = "none";
			if (document.getElementById("livetypefile").checked) {
				videoplayer.MediaRecorderStop();
			} else {
				livemediaStop();
			}
			newlivestop();
		}


	</script>


	<script>
		class timNode {
			node = null;
			name = null;
			cover = null;
			fnode = null;
			constructor(node, name, cover, fnode) {
				this.node = node;
				this.name = name;
				this.cover = cover+"?mode/2/w/120";
				this.fnode = fnode;
			}
		}

		let tls = true;
		let port = 443;
		if (window.location.protocol === 'http:') {
			tls = false;
			port = 5081;
		}

		var tc = new timClient(tls, window.location.hostname, port);

		var friendMap = new Map();
		var timNodeMap = new Map();
		var messageMap = new Map();
		var midMap = new Map();
		var newMesMap = new Map();
		var alertMap = new Map();
		var roomMap = new Map();
		var videoroomMap = new Map();
		var roomMemberMap = new Map();
		var rosterBarMap = new Map();

		//ack message from server 服务反馈的信息
		tc.ackHandler = function (data) {
			let ta = new TimAck();
			ta = JSON.parse(data);
			switch (ta.timType) {
				case STAT.TIMMESSAGE:
					if (!ta.ok) { //not ok 表示信息发送失败(注意，信息发送成功是没有ack的，服务器会推送发送用户的信息回来，信息会带上时间与id)
						alert("发信失败：" + ta.error.info);
					}
					break;
				case STAT.TIMPRESENCE:
					if (!ta.ok) { //not ok，表示状态信息发送失败(注意，状态信息发送成功是没有ack的)
						console.log("send presence failed>>", ta.error.code, ":", ta.error.info);
					}
					break;
				case STAT.TIMLOGOUT: // 强制下线
					tc.Logout(); // 收到强制下线的指令后，主动退出登录
					break;
				case STAT.TIMAUTH:
					if (ta.ok) { // 登录成功
						myAccount = ta.n;
						tc.UserInfo([myAccount]);//拉取自己账号信息
						tc.Roster();                                     //Pull the roster 拉取花名册
						sleep(1000).then(() => tc.UserRoom());           //pull the account of group 拉取群账号
						// tc.BlockRoomList();                           //blocklist of user group 用户群黑名单
						// tc.BlockRosterList();                         //blocklist of user 用户黑名单
						tc.OfflineMsg();								 //when login successful, get the offline message 登录成功后，拉取离线信息
						$("#modal-login").modal("hide");
					} else {
						tc.Logout();
						console.log("login failed and logout");
						if (ta.error.code == STAT.ERR_AUTH) {
							alert("用户名密码错误");
						} else if (ta.error.code == STAT.ERR_OVERENTRY) {
							alert("同账号登录超过限制数");
						} else if (ta.error.code == STAT.ERR_TOKEN) {
							if (!isEmpty(tokenData)) {
								loginBytoken(tokenData)
							}
						} else {
							alert("登录失败");
						}
						initloginflag(false, "");
					}
					break;

				case STAT.TIMREVOKEMESSAGE:
					if (ta.ok) {
						let tonode = ta.n;
						let t = ta.t;
						let mid = ta.t2;
						let cid = null;
						if (t == 2) {
							cid = chatid(myAccount, tonode)
							let m = messageMap.get(cid)
							if (m.get(mid)) {
								m.get(mid).isRevoke = true;
							}
						} else if (t == 3) {
							cid = chatid(tonode, tonode)
							let m = messageMap.get(cid)
							if (m.get(mid)) {
								m.get(mid).isRevoke = true;
							}
						}
						if (accountId.value == tonode) {
							showchat(cid, false)
						}
					}
					break;
				case STAT.TIMBURNMESSAGE:
					break;
				// 虚拟房间操作回馈信息
				case STAT.TIMVROOM:
					if (ta.ok) {
						switch (ta.t) {
							case STAT.VRITURLROOM_REGISTER: //注册虚拟房间成功
								console.log("register vriturl room ok>>", ta.t, " >>", ta.n);
								break;
							case STAT.VRITURLROOM_REMOVE: //删除虚拟房间成功
								console.log("remove vriturl room ok>>", ta.t, " >>", ta.n);
								break;
							case STAT.VRITURLROOM_ADDAUTH: //加权成功
								console.log("add auth to vriturl room ok>>", ta.t, " >>", ta.n);
								break;
							case STAT.VRITURLROOM_RMAUTH: //去权成功
								console.log("cancel auth vriturl room process >>", ta.t, " >>", ta.n);
								break;
							case STAT.VRITURLROOM_SUB: //订阅成功
								if (bigDataStreamEvent.get(ta.n) == 1) {
									subVnode = ta.n;
									bigDataStreamEvent.set(ta.n, liveEvent)
								} else if (bigDataStreamEvent.get(ta.n) == 2) {
									bigDataStreamEvent.set(ta.n, recvfileEvent)
								}
								break;
							case STAT.VRITURLROOM_SUBCANCEL: //取消订阅成功
								break;
							default:
								console.log("vriturl room process ok>>", ta.t, " >>", ta.n);
						}
					} else {
						console.log("vriturl room process failed:", ta.error.code, ":", ta.error.info);
					}
					break;
				/*************************************************************/
				case STAT.TIMBUSINESS: //业务操作回馈
					if (ta.ok) {
						switch (ta.t) {
							case STAT.BUSINESS_MODIFYAUTH:
								unmodifyPwd();
								alert("修改密码成功");
								break;
							case STAT.BUSINESS_REMOVEROSTER: //删除好友成功
								let n = timNodeMap.get(ta.n)
								alert("你与[" + n.name + "]已成功解除好友关系");
								timNodeMap.delete(ta.n)
								friendMap.delete(ta.n)
								rosterBarMap.delete(ta.n);
								showRosterBarList();
								break;
							case STAT.BUSINESS_BLOCKROSTER: //拉黑好友成功
								console.log("block successful:", ta.n);
								break;
							case STAT.BUSINESS_NEWROOM: //新建群组成功
								tc.RoomInfo([ta.n])
								tc.RoomUsers(ta.n)
								break;
							case STAT.BUSINESS_ADDROOM: //
								tc.RoomInfo([ta.n])
								tc.RoomUsers(ta.n)
								tc.StreamToRoom(ta.n, null, 1, 0);
								let alertfunc = () => {
									alert("你已加入群：" + timNodeMap.get(ta.n).name)
								}
								let conditionfunc = () => {
									return timNodeMap.get(ta.n)
								}
								DoWhile(conditionfunc, alertfunc, 5)
							case STAT.BUSINESS_PASSROOM: //申请加入群成功
								tc.RoomInfo([ta.n])
								tc.RoomUsers(ta.n)
								break;
							case STAT.BUSINESS_NOPASSROOM: //申请加入群不成功
								console.log("reject group successful:", ta.n);
								break;
							case STAT.BUSINESS_PULLROOM: //拉人入群
								tc.RoomInfo([ta.n])
								tc.RoomUsers(ta.n)
								break;
							case STAT.BUSINESS_KICKROOM: //踢人出群
								groupmember(ta.n);
								break;
							case STAT.BUSINESS_BLOCKROOM:
								console.log("block the group successful:", ta.n)
								break;
							case STAT.BUSINESS_LEAVEROOM: //退群
								roomMap.delete(ta.n);
								roomMemberMap.delete(ta.n)
								rosterBarMap.delete(ta.n);
								if (videoroomMap.has(ta.n)) {
									videoroomMap.delete(ta.n)
									videoroomlist()
								}
								myroom();
								showRosterBarList();
								alert("退群成功");
								break;
							case STAT.BUSINESS_CANCELROOM: //注销群
								console.log("cancel group successful:", ta.n);
								break;
							default:
								console.log("business successful >>", ta);
						}
					} else {
						switch (ta.t) {
							case STAT.BUSINESS_MODIFYAUTH:
								alert("修改密码失败");
								break;
						}
						console.log("business process failed:", ta.error.code, ":", ta.error.info);
					}
					break;
				case STAT.TIMNODES:
					if (ta.ok) {
						switch (ta.t) {
							case STAT.NODEINFO_MODIFYUSER:
								console.log("modify userinfo successful");
								break;
							case STAT.NODEINFO_MODIFYROOM:
								console.log("modify roominfo successful");
								break;
						}
					}
			}
		};

		//TimMessage 处理handler
		tc.messageHandler = function (data) {
			let tm = new TimMessage();
			tm = JSON.parse(data);
			parseMessage(tm, false);
		};

		//记录状态订阅者
		var submap = {};
		//状态消息
		tc.presenceHandler = function (data) {
			let tp = new TimPresence()
			tp = JSON.parse(data);
			if (tp.subStatus == 1) {
				tc.PresenceToUser(tp.fromTid.node, 0, userstatus, 2, null, null);
			}
			if (tp.subStatus == 1 || tp.subStatus == 2) {
				if (isEmpty(submap[tp.fromTid.node])) {
					submap[tp.fromTid.node] = 0;
				}
			}
			if (tp.subStatus == 3) {
				tc.UserInfo([tp.fromTid.node]);
				return
			}

			if (tp.offline && (tp.fromTid.node == myAccount)) {
				tc.BroadPresence(1, 0, userstatus);
			}
			if (tp.offline) {
				friendstatus(tp.fromTid.node, true, "");
				if (videoPhoneAccount == tp.fromTid.node) {
					alert("对方掉线了");
					videophoneStop();
				}
			} else {
				friendstatus(tp.fromTid.node, false, tp.status);
			}
		};

		tc.offlineMsgHandler = function (data) {
			let tml = new TimMessageList();
			tml = JSON.parse(data);
			if (!isEmpty(tml) && !isEmpty(tml.messageList)) {
				sleep(1000).then(() => {
					addmsg(tml.messageList, false)
				})
			}
		};

		tc.offlinemsgEndHandler = function (data) {
			tc.BroadPresence(1, 0, userstatus);//when finish offline message recive,subcript and broad the presence
		};

		tc.pullmessageHandler = function (data) {
			let tml = new TimMessageList();
			tml = JSON.parse(data);
			if (!isEmpty(tml) && !isEmpty(tml.messageList)) {
				sleep(500).then(() => {
					addmsg(tml.messageList, true)
				})
			}
		};

		function addmsg(msglist, ispull) {
			let p = true;
			for (const u of msglist) {
				let v = timNodeMap.get(u.fromTid.node);
				if (isEmpty(v)) {
					tc.UserInfo([u.fromTid.node]);
					p = false
				}
			}
			if (!p) {
				sleep(500).then(() => addmsg(msglist, ispull));
				return
			}
			for (let i = msglist.length - 1; i >= 0; i--) {
				parseMessage(msglist[i], ispull);
			}
		}

		tc.bigBinaryHandler = (data) => {
			let idx = 0;
			for (let i = 0; i < data.length; i++) {
				if (data[i] == STAT.SEQ_BIN) {
					idx = i;
					break
				}
			}
			let node = uint8ArrayToString(data.slice(0, idx));
			if (node == videoPhoneAccount) {
				if (data.length < 1000) {
					return
				}
				document.getElementById("readylink").innerText = "";
				let databs = data.slice(idx + 1, data.length);
				if (videophone) {
					videophone.AddVideoUint8Array(ungzip(databs))
				}
			}
		}


		tc.bigBinaryStreamHandler = (data) => {
			let idx = 0;
			for (let i = 0; i < data.length; i++) {
				if (data[i] == STAT.SEQ_BIN) {
					idx = i;
					break
				}
			}
			let node = uint8ArrayToString(data.slice(0, idx));
			let databs = data.slice(idx + 1, data.length);
			let e = bigDataStreamEvent.get(node)
			if (!isEmpty(e) && e != 1 && e != 2) {
				e(databs, node)
			}
		}

		tc.nodesHandler = function (data) {
			let tn = new TimNodes();
			tn = JSON.parse(data);
			switch (tn.ntype) {
				case STAT.NODEINFO_ROSTER: //花名册返回
					if (!isEmpty(tn.nodelist)) {
						tc.UserInfo(tn.nodelist); //获取用户详细资料
						for (const item of tn.nodelist) {
							friendMap.set(item, 1);
						}
					}
					break;
				case STAT.NODEINFO_ROOM: //用户的群账号返回
					if (!isEmpty(tn.nodelist)) {
						tn.nodelist.forEach((node) => {
							roomMap.set(node, 1);
							tc.RoomUsers(node); //获取群的成员
						})
						tc.RoomInfo(tn.nodelist); //获取群详细资料
					}
					break;
				case STAT.NODEINFO_ROOMMEMBER: //群成员账号返回
					if (!isEmpty(tn.nodelist)) {
						roomMemberMap.set(tn.node, tn.nodelist)
						tc.UserInfo(tn.nodelist);
					}
					break;
				case STAT.NODEINFO_USERINFO: //用户信息返回
					for (const [k, v] of Object.entries(tn.usermap)) {
						if (myAccount == k) {
							online(v.cover, replace(v.name));
							timNodeMap.set(k, new timNode(k, replace(v.name), v.cover, k));
						} else {
							timNodeMap.set(k, new timNode(k, replace(v.name), v.cover, k));
							if (!isEmpty(rosterBarMap.get(k))) {
								showRosterBarList();
							} else {
								addToNodelist(k, false);
							}
						}
					}
					break;
				case STAT.NODEINFO_ROOMINFO: //群信息返回
					for (const [k, v] of Object.entries(tn.roommap)) {
						roomMap.set(k, new timNode(k, replace(v.topic), v.cover, v.founder));
						timNodeMap.set(k, new timNode(k, replace(v.topic), v.cover, v.founder));
						addToNodelist(k, true);
						if (v.kind == 1) {
							videoroomMap.set(k, new timNode(k, replace(v.topic), v.cover, v.founder));
						}
					}
					break;
				case STAT.NODEINFO_BLOCKROSTERLIST: //用户黑名单
					break;
				case STAT.NODEINFO_BLOCKROOMLIST: //用户拉黑群的群账号
					break;
				case STAT.NODEINFO_BLOCKROOMMEMBERLIST: //群拉黑账号名单
					break;
			}
		};

		function parseMessage(tm, isPull) {
			//message消息
			if (tm.msType == 1) {
				console.log("this is system message >>", tm);
			} else if (tm.msType == 2) {
				// console.log("this is user to user message");
			} else if (tm.msType == 3) {
				// console.log("this is room to user message");
			}
			if (tm.msType != 1) {
				switch (tm.odType) {
					case 1: //常规消息
						let id = 0;
						if (tm.msType == 2) {
							id = chatid(tm.fromTid.node, tm.toTid.node);
						} else {
							id = chatid(tm.roomTid.node, tm.roomTid.node);
						}
						if (isEmpty(messageMap.get(id))) {
							initArrays(id);
						}
						if (tm.msType == 2) {
							appendmsg(id, tm.mid, tm.fromTid.node, tm.toTid.node, false, tm, isPull);
						} else {
							appendmsg(id, tm.mid, tm.fromTid.node, tm.roomTid.node, true, tm, isPull);
						}
						if (isPageHidden()) {
							flashTitle("新消息");
						}
						break;
					case 2: //撤回消息
						let cid = 0;
						let node = "";
						if (tm.msType == 2) {
							node = tm.fromTid.node
							cid = chatid(node, tm.toTid.node);
						} else if (tm.msType == 3) {
							node = tm.roomTid.node;
							cid = chatid(node, node);
						}
						let mm = messageMap.get(cid);
						if (mm) {
							let m = mm.get(tm.mid);
							if (m) {
								m.isRevoke = true;
							}
						}
						if (accountId.value == node) {
							showchat(cid, false)
						}
						break;
					case 3: //阅后即焚
						break;
					case 4: //业务消息
						switch (tm.bnType) {
							case STAT.BUSINESS_REMOVEROSTER:
								friendMap.delete(tm.fromTid.node)
								rosterBarMap.delete(tm.fromTid.node);
								showRosterBarList();
								break;
							case STAT.BUSINESS_ADDROSTER:
								addfriendAlert(tm.fromTid.node, tm.dataString);
								break;
							case STAT.BUSINESS_FRIEND:
								let faccount = "";
								if (tm.fromTid.node != myAccount) {
									faccount = tm.fromTid.node;
								} else {
									faccount = tm.toTid.node;
								}
								friendMap.set(faccount, 1);
								tc.UserInfo([faccount]);
								sleep(1000).then(() => tc.PresenceToUser(faccount, 0, "😄", 1, null, null))
								befriendAlert(faccount);
								break;
							case STAT.BUSINESS_ADDROOM:
								addroomAlert(tm.fromTid.node, tm.roomTid.node, tm.dataString);
								break;
							case STAT.BUSINESS_PASSROOM:
								tc.StreamToRoom(tm.roomTid.node, null, 1, 0);
								// tc.UserRoom();
								tc.RoomInfo([tm.roomTid.node]);
								tc.RoomUsers(tm.roomTid.node);
								sleep(2000).then(() => {
									if (roomMap.get(tm.roomTid.node)) {
										alert("入群申请已经通过[" + roomMap.get(tm.roomTid.node).name + "]")
									}
								})
								break
							case STAT.BUSINESS_PULLROOM:
								tc.RoomInfo([tm.roomTid.node]);
								tc.RoomUsers(tm.roomTid.node);
								sleep(2000).then(() => {
									if (roomMap.get(tm.roomTid.node)) {
										alert("你已经被拉入群[" + roomMap.get(tm.roomTid.node).name + "]")
									}
								})
								break;
							case STAT.BUSINESS_KICKROOM:
								let r = roomMap.get(tm.roomTid.node);
								if (!isEmpty(r)) {
									alert("你已经被移除出群：" + r.name);
									roomMap.delete(tm.roomTid.node);
									videoroomMap.delete(tm.roomTid.node)
									roomMemberMap.delete(tm.roomTid.node);
									rosterBarMap.delete(tm.roomTid.node);
									myroom();
									showRosterBarList();
								} else {
									alert("你已经被移除出群：" + tm.roomTid.node);
								}
								break
							case STAT.BUSINESS_NOPASSROOM:
								alert("入群申请没有通过:" + tm.roomTid.node);
								break
							default:
								console.log("default>>>");
						}
						break;
					case 5: //流数据
						//流数据转字节数组
						if (tm.udshow == 1) {
							tc.RoomUsers(tm.roomTid.node);
						} else if (tm.udshow == 2) {
							sleep(2000).then(() => tc.RoomUsers(tm.roomTid.node));
						} else if (tm.udshow == 3) {
							sleep(2000).then(() => tc.RoomUsers(tm.roomTid.node));
						}

						//视频电话处理
						if (tm.udtype == 1) {
							let n = timNodeMap.get(tm.fromTid.node);
							let t = Date.now();
							if (!isEmpty(n)) {
								if (confirm("确定连接[" + n.name + "]的视频电话?")) {
									if (Date.now() - t > 30000) {
										alert("视频连接已经超时");
										return;
									}
									if (isEmpty(videoPhoneAccount))
										tc.StreamToUser(tm.fromTid.node, null, 0, 2);
								} else {
									tc.StreamToUser(tm.fromTid.node, null, 0, 7);
								}
							}
						} else if (tm.udtype == 2) {
							if (videoPhoneAccount == tm.fromTid.node) {
								tc.StreamToUser(tm.fromTid.node, null, 0, 3);
							} else {
								tc.StreamToUser(tm.fromTid.node, null, 0, 6);
							}
						} else if (tm.udtype == 3) {
							if (isEmpty(videoPhoneAccount)) {
								videoPhoneAccount = tm.fromTid.node;
								tc.StreamToUser(tm.fromTid.node, null, 0, 4);
								timvideoshow()
								videophoneStart();
								sleep(1000).then(() => pushVideoPhoneStream())
							} else {
								tc.StreamToUser(tm.fromTid.node, null, 0, 5);
							}
						} else if (tm.udtype == 4) {
							if (tm.fromTid.node == videoPhoneAccount) {
								pushVideoPhoneStream();
							}
						} else if (tm.udtype == 5) {
							if (tm.fromTid.node == videoPhoneAccount) {
								videoPhoneAccount = "";
								alert("对方通话中");
							}
						} else if (tm.udtype == 6) {
							if (tm.fromTid.node == videoPhoneAccount) {
								videoPhoneAccount = "";
								if (!isEmpty(videophone)) {
									videophoneStop()
								}
								alert("对方已经断开视频连接");
							}
						} else if (tm.udtype == 7) {
							if (tm.fromTid.node == videoPhoneAccount) {
								videoPhoneAccount = "";
								if (!isEmpty(videophone)) {
									videophoneStop()
								}
								alert("对方已取消了电话连接");
							}
						} else if (tm.udtype == 11) {
							let node = tm.fromTid.node;
							if (!isEmpty(timNodeMap.get(node))) {
								let name = timNodeMap.get(node).name;
								let jf = JSON.parse(tm.dataString);
								s = "[" + name + "] 发送文件：\n" + jf.name + "\n 文件大小：" + (jf.size / MB) + "M"
								if (confirm(s, "\n是否接收")) {
									recvfileMap.set(jf.vnode, new fileBean(jf.name, jf.size, tm.fromTid.node));
									bigDataStreamEvent.set(jf.vnode, 2)
									tc.VirtualroomSubBinary(jf.vnode)
									showsendToast();
									sleep(1000).then(() => tc.StreamToUser(node, jf.vnode, 0, 13))
								} else {
									tc.StreamToUser(node, jf.vnode, 0, 12);
								}
							}
						} else if (tm.udtype == 12) {
							alert("对方暂时无法接收文件")
							sendfileMap.delete(tm.dataString)
						} else if (tm.udtype == 13) {
							sendfilestart(tm.dataString);
						} else if (tm.udtype == 14) {
							recvfileMap.delete(tm.dataString)
							alert("对方取消了文件发送");
						} else if (tm.udtype == 15) {
							cancelsendfile(tm.dataString, false);
							alert("对方取消了文件接收");
						} else if (tm.udtype == 21) {
							if (tm.roomTid.node == mutlivideostreamid.value) {
								let vnode = tm.dataString;
								storeVideoRoom(tm.fromTid.node, vnode)
								bigDataStreamEvent.set(vnode, roomliveEvent)
								if (!isEmpty(liveroomvideo)) {
									tc.StreamToRoom(tm.roomTid.node, webtimVnode, 0, 22)
								} else if (!isEmpty(liveroomAudio)) {
									tc.StreamToRoom(tm.roomTid.node, webtimVnode, 0, 32)
								}
							}
						} else if (tm.udtype == 22) {
							if (tm.roomTid.node == mutlivideostreamid.value) {
								let vnode = tm.dataString;
								storeVideoRoom(tm.fromTid.node, vnode)
								bigDataStreamEvent.set(vnode, roomliveEvent)
							}
						} else if (tm.udtype == 31) {
							if (tm.roomTid.node == mutlivideostreamid.value) {
								let vnode = tm.dataString;
								storeAudioRoom(tm.fromTid.node, vnode)
								bigDataStreamEvent.set(vnode, roomliveEvent)
								if (!isEmpty(liveroomvideo)) {
									tc.StreamToRoom(tm.roomTid.node, webtimVnode, 0, 22)
								} else if (!isEmpty(liveroomAudio)) {
									tc.StreamToRoom(tm.roomTid.node, webtimVnode, 0, 32)
								}
							}
						} else if (tm.udtype == 32) {
							if (tm.roomTid.node == mutlivideostreamid.value) {
								let vnode = tm.dataString;
								storeAudioRoom(tm.fromTid.node, vnode)
								bigDataStreamEvent.set(vnode, roomliveEvent)
							}
						} else if (tm.udtype == 33) {
							let mv = mulMediaroommap.get(tm.dataString);
							if (mv) {
								if (mv.unode == tm.fromTid.node) {
									mv.close()
									mulMediaroommap.delete(tm.dataString)
								}
							}
						} else if (tm.udtype == 34) {
							let mv = mulMediaroommap.get(tm.dataString);
							if (mv) {
								if (mv.unode == tm.fromTid.node) {
									mv.notify();
								}
							}
						}
						break;
					default: //开发者自定义的消息
						console.log("other message>>>", tm);
				}
			} else {
				alert("系统通知：" + tm.dataString);
			}
		}

	</script>
	<script>

		function online(icon, name) {
			document.getElementById("icon").src = icon;
			document.getElementById("loginicon").src = icon;
			initloginflag(true, name,)
		}

		// function failedSend(msg) {
		// 	var div = document.createElement('div');
		// 	div.innerHTML =
		// 		'<div class="chat-content-rightside"><div class="d-flex ms-auto"><div class="flex-grow-1 me-2">' +
		// 		'<p class="mb-0 chat-time text-end">' + datetime() +
		// 		'</p><p class="chat-right-msg text-light">' + msg + '</p></div></div></div>';
		// 	document.getElementById("containerId").appendChild(div);
		// }

		function initArrays(cid) {
			let arrays = [];
			arrays.push('<div class="ps__rail-x" style="left: 0px; bottom: 0px;">'
				+ '<div class="ps__thumb-x" tabindex="0" style="left: 0px; width: 0px;"></div>'
				+ '</div>'
				+ '<div class="ps__rail-y" style="top: 0px; height: 520px; right: 0px;">'
				+ '<div class="ps__thumb-y" tabindex="0" style="top: 0px; height: 235px;"></div>'
				+ '</div>')
			messageMap.set(cid, new MessageWare(cid))
		}

		async function appendmsg(cid, mid, fnode, rnode, isRoom, tm, isPull) {
			let v = timNodeMap.get(fnode);
			let mm = messageMap.get(cid)
			mm.addMessage(new Message(tm, isRoom, cid))
			let tonode = accountId.value;
			let show = false;
			let shownode = fnode;
			let rn = fnode;
			if (isRoom) {
				if (cid == chatid(tonode, tonode)) {
					show = true;
				}
				shownode = rnode;
				rn = rnode;
			} else {
				if (rn == myAccount) {
					rn = tm.toTid.node
				}
				if (cid == chatid(myAccount, tonode)) {
					show = true;
				}
			}
			let bar = rosterBarMap.get(rn);
			if (!isEmpty(bar)) {
				bar.timestamp = unixnanoToDatatime(tm.timestamp);
				bar.updatetime(bar.timestamp)
			}
			if (show) {
				showchat(cid, false);
				if (!isEmpty(bar)) {
					bar.updatestat("")
				}
			} else if (!isPull) {
				if (!isEmpty(bar)) {
					bar.updatestat("&#128308;有新消息")
				}
			}
		}

		function initshowbar(v) {
			if (initcount > 0) {
				showRosterBarList()
				initcount = 0;
			}
		}

		let containerId = document.getElementById("containerId")
		let msgnodelist = document.getElementById("msgnodelist")
		let accountId = document.getElementById("accountId")

		async function clear() {
			alertMap.clear();
			friendMap.clear();
			timNodeMap.clear();
			messageMap.clear();
			newMesMap.clear();
			rosterBarMap.clear();
			roomMap.clear();
			roomMemberMap.clear();
			videoroomMap.clear();
			recvfileMap.clear();
			sendfileMap.clear();
			mulMediaroommap.clear();
			bigDataStreamEvent.clear();
			msgnodelist.innerHTML = "";
			containerId.innerHTML = "";
			accountId.innerHTML = "";
			document.getElementById("iconuser").src = "/img/user.png";
			document.getElementById("touser").innerHTML = "";
		}

		async function showchat(cid, bm) {
			let mm = messageMap.get(cid);
			if (!isEmpty(mm)) {
				let arrays = [];
				for (const m of mm.toArray()) {
					let s = await m.tomsg()
					arrays.push(s);
				}
				containerId.innerHTML = arrays.join('');
				let scrollHeight = containerId.scrollHeight;
				let scrollTop = containerId.scrollTop;
				if (bm) {
					containerId.scrollTo(0, scrollHeight);
				} else if (scrollTop <= 100) {
					containerId.scrollTo(0, 0);
				} else {
					containerId.scrollTo(0, scrollHeight);
				}
				hideDataLoading()
			} else {
				containerId.innerHTML = "";
			}
		}

		listen()
		function listen() {
			['touchmove', 'touchstart', 'touchend', 'touchcancel'].forEach(eventName => {
				msgnodelist.addEventListener(eventName, function (event) {
					if (event.changedTouches && event.type === 'touchmove') {
						if (isScrolling) {
							event.preventDefault();
							let currentY = event.touches[0].screenY;
							let deltaY = currentY - startY2;
							msgnodelist.scrollTop += deltaY;
							startY2 = currentY;
						}
					} else if (event.changedTouches && event.type === 'touchstart') {
						startY2 = event.touches[0].screenY;
						isScrolling = true;
					} else if (event.changedTouches && (event.type === 'touchend' || event.type === 'touchcancel')) {
						isScrolling = false;
					}
				})
			})
			let isScrolling = false;
			let accountIdisTop = false;
			let startY = 0;
			let startY2 = 0;
			['scroll', 'wheel', 'touchmove', 'touchstart'].forEach(eventName => {
				containerId.addEventListener(eventName, function (event) {
					if (event.wheelDelta) {
						event.preventDefault();
						var isScrollingUp = event.wheelDelta > 0;
					} else if (event.changedTouches && event.type === 'touchmove') {
						event.preventDefault();
						const touch = event.touches[0];
						let currentY = touch.clientY;
						let deltaY = currentY - startY;
						if ((currentY - startY) > 0) {
							isScrollingUp = true
						}
					} else if (event.changedTouches && event.type === 'touchstart') {
						startY = event.touches[0].clientY;
					} else if (event.target && event.type === 'scroll') {
						event.preventDefault();
						accountIdisTop = event.target.scrollTop === 0;
					}
					if (isScrollingUp) {
						if (accountIdisTop) {
							pull();
						}
					}
				});
			});
		}
		const pull = debounce(pullHandler, 200);
		function pullHandler() {
			let account = accountId.value;
			if (friendMap.has(account)) {
				let mm = messageMap.get(chatid(account, myAccount))
				if (mm && mm.size() >= 10 && !mm.end()) {
					tc.PullUserMessage(account, mm.minMid, 5)
					showDataLoading()
				} else if (mm.end()) {
					alertTop()
				}
			} else if (roomMap.has(account)) {
				let mm = messageMap.get(chatid(account, account))
				if (mm && mm.size() >= 10 && !mm.end()) {
					tc.PullRoomMessage(account, mm.minMid, 5)
					showDataLoading()
				} else if (mm.end()) {
					alertTop()
				}
			}
		}

		const modal = new bootstrap.Modal(document.getElementById('dataLoadingModal'), { backdrop: false });
		const lastItemModal = new bootstrap.Modal(document.getElementById('lastItemModal'), { backdrop: false });
		function showDataLoading() {
			modal.show();
			sleep(5000).then(() => hideDataLoading())
		}

		function hideDataLoading() {
			modal.hide();
		}

		function alertTop() {
			lastItemModal.show();
			sleep(1000).then(() => lastItemModal.hide())
		}

		function addToNodelist(account, isRoom) {
			if (!isEmpty(rosterBarMap.get(account))) {
				return
			}
			if (isEmpty(friendMap.get(account)) && isEmpty(roomMap.get(account))) {
				return
			}
			let v = timNodeMap.get(account);
			if (isEmpty(v)) {
				sleep(1000).then(() => addToNodelist(account, isRoom));
				return
			}
			rosterBarMap.set(account, new MessageBar(account, true, isRoom, ""))
			showRosterBarList()
			if (isRoom) {
				tc.PullRoomMessage(account, 0, 10)
			} else {
				tc.PullUserMessage(account, 0, 10)
			}
		}

		async function showRosterBarList() {
			let msgnodelist = document.getElementById("msgnodelist")
			msgnodelist.innerHTML = ""
			for (let v of rosterBarMap.values()) {
				msgnodelist.appendChild(v.tomsg());
			}
		}

		function friendstatus(account, on, show) {
			let u = rosterBarMap.get(account)
			if (isEmpty(u)) {
				sleep(1000).then(() => friendstatus(account, on, show))
				return
			}
			u.offline = on;
			u.show = show;
			showRosterBarList();
		}

		function send() {
			let msg = document.getElementById("msgid").value;
			let node = accountId.value;
			showClose();
			if (isEmpty(node)) {
				alert("请先选择聊天对象");
				return
			}
			try {
				if (!isEmpty(msg)) {
					if (!isEmpty(friendMap.get(node))) {
						tc.MessageToUser(node, msg, 0, 0, null, null);
					} else if (!isEmpty(roomMap.get(node))) {
						tc.MessageToRoom(node, msg, 0, 0, null, null);
					} else {
						alert("账号错误");
					}
				}
			} catch (err) {
				alert("信息发送失败");
				return
			}
			document.getElementById("msgid").value = "";
		}

		$("#msgid").keydown(function () {
			if (event.keyCode == "13") {
				send();
			}
		})

		$("#password").keydown(function () {
			if (event.keyCode == "13") {
				login();
			}
		})

		function initloginflag(_login, name) {
			if (!_login) {
				document.getElementById("loginflag").innerText = "未登录";
				document.getElementById("icon").src = "/img/user.png";
				document.getElementById("loginicon").src = "/img/user.png";
				document.getElementById("logoutId").style.display = "none";
			} else {
				let status = '<select style="font-size:25px;border: none; outline: none;" class="bg-white">'
					+ '<option value="&#128512;" >&#128512;开心</option>'
					+ '<option value="&#128533;" >&#128533;发呆</option>'
					+ '<option value="&#128548;" >&#128548;生气</option>'
					+ '<option value="&#128557;" >&#128557;伤心</option>'
					+ '<option value="&#128561;" >&#128561;心慌</option>'
					+ '<option value="&#129319;" >&#129319;生病</option>'
					+ '<option value="&#128554;" >&#128554;困</option>'
					+ '<option value="&#9917;" >&#9917;运动</option>'
					+ '<option value="&#12953;" >&#12953;秘密</option>'
					+ '</select>';
				document.getElementById("loginflag").innerHTML = name + ' <span onchange="status(this)">' + status + '</span>'
				document.getElementById("logoutId").style.display = "inline";
				// document.getElementById("usertitle").innerText = "";
				document.getElementById("username").value = "";
				document.getElementById("password").value = "";
			}
		}
		function status(t) {
			let s = t.querySelector("select").value
			if (s) {
				userstatus = s;
				tc.BroadPresence(4, 0, s);
			}
		}


		function logout() {
			if (!confirm("确定退出登录")) {
				return
			}
			initloginflag(false, "");
			myAccount = null;
			tc.Logout();
			clear();
			$("#modal-control").modal("hide");
			modalLogin();
		}

		var webtimToken = null;
		var tokenData = null;
		var userstatus = "&#128512;"
		function login() {
			var username = document.getElementById("username").value;
			var pwd = document.getElementById("password").value;
			if (username == "" || pwd == "") {
				alert("用户名密码不能为空");
				return
			}
			clear();
			let data = $("#loginform").serialize();
			loginBytoken(data);
		}

		function loginBytoken(tdata) {
			httpPost("token", tdata, (data) => {
				if (!isEmpty(data)) {
					let jsonres = JSON.parse(data);
					if (jsonres.ok) {
						tokenData = tdata;
						webtimToken = jsonres.token;
						tc.LoginByToken(webtimToken, "web", 1);
						hideSidebar();
					} else {
						alert("登录失败");
					}
				}
			});
		}

		function peer(account, isRoom) {
			accountId.value = account;
			let cid = chatid(account, myAccount);
			if (isRoom) {
				cid = chatid(account, account);
			}
			let arrays = messageMap.get(cid);
			if (isEmpty(arrays)) {
				initArrays(cid);
			}
			showchat(cid, true);
			if (!isEmpty(rosterBarMap.get(account))) {
				rosterBarMap.get(account).updatestat("")
			}
			let v = timNodeMap.get(account);
			document.getElementById("iconuser").src = v.cover;
			if (isRoom) {
				document.getElementById("touser").innerHTML = "&#128246; " + v.name;
			} else {
				document.getElementById("touser").innerHTML = " " + v.name;
			}
		}

		function hideSidebar() {
			$(".chat-wrapper").removeClass("chat-toggled")
		}

	</script>

	<script>
		var livemedia = null;
		async function liveMediaStart() {
			try {
				let videoliveObject = document.createElement("video");
				videoliveObject.autoplay = true;
				videoliveObject.muted = true;
				playvideowindow.appendChild(videoliveObject);
				videoliveObject.innerHTML = '<source src="video.mp4" type="video/mp4"><source src="video.webm" type="video/webm">'
				livemedia = new MediaRTC(videoliveObject, { video: video, audio: audio });
				// livemedia.recorderHz = 600;
				// livemedia.audioBitsPerSecond = 320000;
				// livemedia.videoBitsPerSecond = 100000;
				await livemedia.MediaDevices();
				livemedia.sendStream = pushLiveVideoStream
				livemedia.Pushstream();
				sleep(2000).then(() => livestart(videoliveObject))
			} catch (err) {
				alert("未获得麦克风或摄像头权限")
			}
		}

		function livemediaStop() {
			if (!isEmpty(livemedia)) {
				livemedia.MediaRecorderStop();
			}
			livemedia = null;
		}

		function pushLiveVideoStream(buffer) {
			if (!isEmpty(webtimVnode)) {
				sendLiveStream(buffer, webtimVnode)
			}
		}
	</script>


	<script>
		const sendVData = (buffer, tonode) => {
			tc.BigDataBinary(tonode, gzip(buffer))
		}

		const myvideo = document.getElementById("myvideo");
		let videoPhoneAccount = "";
		let videophone = null;
		let media = null;

		function videophoneStart() {
			try {
				myvideo.innerHTML = '<source src="video.mp4" type="video/mp4"><source src="video.webm" type="video/webm">'
				media = new MediaRTC(myvideo, { video: video, audio: audio });
				// media.recorderHz = 300;
				// media.audioBitsPerSecond = 320000;
				// media.videoBitsPerSecond = 100000;
				media.MediaDevices();
				media.Pushstream();
				videophone = new VideoJoin(document.getElementById("videophone"));
				videophone.modeRealtime = true;
				// videophone.modeSpeedRateup = true;
				videophone.style = "width: 250px;height: auto;";
			} catch (err) {
				alert(err)
			}
		}

		function videophoneStop() {
			if (!isEmpty(media)) {
				media.MediaRecorderStop();
			}
			if (!isEmpty(videoPhoneAccount)) {
				tc.StreamToUser(videoPhoneAccount, null, 0, 6);
			}
			if (!isEmpty(videophone)) {
				videophone.Clear();
			}
			videoPhoneAccount = "";
			media = null;
			videophone = null;
		}

		function pushVideoPhoneStream() {
			media.sendStream = pushMediaRecorderStream
		}

		function pushMediaRecorderStream(buffer) {
			if (!isEmpty(videoPhoneAccount)) {
				sendVData(buffer, videoPhoneAccount)
			}
		}

	</script>

	<script>
		function addfriendAlert(account, msg) {
			let s = '<div class="alert alert-info alert-dismissible fade show">'
				+ '<button type="button" class="btn-close" data-bs-dismiss="alert" onclick=\'rmAlertMap("' + account + '")\'></button>'
				+ '<strong>请求加为好友：' + msg + '</strong>'
				+ '<button type="button"  class="btn btn-primary"  onclick=\'agreeAddroster("' + account + '")\'>同意</button>'
				+ '</div>'
			let id = uuid(account + "_" + myAccount);
			alertMap.set(id, s)
			let alertstring = [];
			for (let v of alertMap.values()) {
				alertstring.push(v);
			}
			document.getElementById("toastshow").innerHTML = alertstring.join("");
		}


		function agreeAddroster(account) {
			let id = uuid(account + "_" + myAccount);
			tc.Addroster(account, "");
			alertMap.delete(id);
			let alertstring = [];
			for (let v of alertMap.values()) {
				alertstring.push(v);
			}
			document.getElementById("toastshow").innerHTML = alertstring.join("");
		}

		function addroomAlert(fnode, rnode, msg) {
			let f = timNodeMap.get(fnode);
			let r = timNodeMap.get(rnode);
			let s = '<div class="alert alert-info alert-dismissible fade show">'
				+ '<button type="button" class="btn-close" data-bs-dismiss="alert" onclick=\'rmRoomAlertMap("' + fnode + '","' + rnode + '")\'></button>'
				+ '<strong>[' + r.name + ']入群申请：' + msg + '</strong>'
				+ '<button type="button"  class="btn btn-primary"  onclick=\'agreeAddroom("' + fnode + '","' + rnode + '")\'>同意</button>'
				+ '</div>'
			let id = uuid(fnode + "_" + rnode + "_" + myAccount);
			alertMap.set(id, s)
			let alertstring = [];
			for (let v of alertMap.values()) {
				alertstring.push(v);
			}
			document.getElementById("toastshow").innerHTML = alertstring.join("");
		}

		function agreeAddroom(fnode, rnode) {
			let id = uuid(fnode + "_" + rnode + "_" + myAccount);
			tc.PullInRoom(rnode, fnode);
			alertMap.delete(id);
			let alertstring = [];
			for (let v of alertMap.values()) {
				alertstring.push(v);
			}
			document.getElementById("toastshow").innerHTML = alertstring.join("");
		}

		function rmAlertMap(account) {
			let id = uuid(account + "_" + myAccount);
			alertMap.delete(id);
		}

		function rmRoomAlertMap(fnode, rnode) {
			let id = uuid(fnode + "_" + rnode + "_" + myAccount);
			alertMap.delete(id);
		}

		function befriendAlert(account) {
			let v = timNodeMap.get(account);
			let alertfunc = () => {
				alert("与[" + timNodeMap.get(account).name + "]成为好友")
			}
			let conditionfunc = () => {
				return timNodeMap.get(account)
			}
			DoWhile(conditionfunc, alertfunc, 10)
		}

	</script>
	<script>
		class Message {
			tm = null;
			isRevoke = false;
			isroom = false
			cid = 0;
			mid = 0;
			constructor(timmessage, isroom, cid) {
				this.tm = timmessage;
				this.isroom = isroom;
				this.cid = cid;
				this.mid = timmessage.mid;
			}
			async tomsg() {
				let v = timNodeMap.get(this.tm.fromTid.node);
				if (isEmpty(v)) {
					let pause = (ms) => { return new Promise(resolve => setTimeout(resolve, ms)) }
					await pause(300)
					return await this.tomsg()
				}
				let s = ""
				let msg = this.tm.dataString;
				let nanotime = this.tm.timestamp;
				let time = unixnanoToDatatime(nanotime)
				let tonode = "";
				let to = ""
				if (this.tm.fromTid.node == myAccount) {
					let revoke = '<a href="javascript:;" onclick="revokemessage(\'' + this.cid + '\',' + this.tm.mid + ')"><B>[撤回]</B></a>';
					if (parseInt(nanotime / 1000000) <= (new Date().getTime() - 60 * 1000)) {
						revoke = "";
					}
					if (this.isroom) {
						tonode = this.tm.roomTid.node;
					} else {
						tonode = this.tm.toTid.node;
					}
					if (this.isRevoke) {
						s = '<div class="chat-content-rightside"><div class="d-flex ms-auto"><div class="flex-grow-1 me-2">' +
							'<p class="mb-0 chat-time text-end">' + time + '</p><span class="chat-right-msg text-dark bg-light"><B>消息已撤回</B></span></div></div></div>';
					} else {
						s = '<div class="chat-content-rightside"><div class="d-flex ms-auto"><div class="flex-grow-1 me-2">' +
							'<p class="mb-0 chat-time text-end">' + time + '</p><span class="chat-right-msg text-dark">' + revoke +
							replace(msg) + '</span></div></div></div>';
					}
				} else {
					if (this.isroom) {
						to = '<B style="color:blue">' + v.name + '</B>,'
					}
					if (this.isRevoke) {
						s = '<div class="chat-content-leftside"><div class="d-flex">' +
							'<img src="' + v.cover + '" width="30" height="30" class="rounded-circle" alt=""><div class="flex-grow-1 ms-2">' +
							'<p class="mb-0 chat-time">' + to + time + '</p>' +
							'<p class="chat-left-msg text-dark bg-light"><B>消息已被撤回</B></p></div></div></div>';
					} else {
						s = '<div class="chat-content-leftside"><div class="d-flex">' +
							'<img src="' + v.cover + '" width="30" height="30" class="rounded-circle" alt=""><div class="flex-grow-1 ms-2">' +
							'<p class="mb-0 chat-time">' + to + time + '</p>' +
							'<p class="chat-left-msg text-dark">' + replace(msg) + '</p></div></div></div>';
					}
				}
				return s;
			}
		}

		function revokemessage(cid, mid) {
			let m = messageMap.get(cid).get(mid);
			if (parseInt(m.tm.timestamp / 1000000) <= (new Date().getTime() - 60 * 1000)) {
				showchat(cid, false);
				alert("超过1分钟，无法撤回")
				return
			}
			if (!confirm("确定撤回消息？")) {
				return
			}
			if (m.isroom) {
				tc.RevokeMessage(mid, null, m.tm.roomTid.node, null, 0, 0);
			} else {
				tc.RevokeMessage(mid, m.tm.toTid.node, null, null, 0, 0);
			}
		}


		class MessageWare {
			minMid = 0;
			cid = 0;
			mset = new SortedSet((a, b) => a.mid - b.mid)
			midmap = new Map();
			endnum = 0;
			constructor(cid) {
				this.cid = cid;
				this.minMid = 0;
			}
			addMessage(msg) {
				if (msg.mid === this.minMid) {
					this.endnum++;
				}
				if (this.minMid == 0 || this.minMid > msg.mid) {
					this.minMid = msg.mid
				}
				if (!this.midmap.has(msg.mid) && msg.mid === this.minMid) {
					this.endnum = 0
				}
				this.mset.add(msg);
				this.midmap.set(msg.mid, msg)
			}
			toArray() {
				return this.mset.toArray();
			}
			get(mid) {
				return this.midmap.get(mid)
			}
			size() {
				return this.midmap.size
			}
			end() {
				return this.endnum > 3
			}
		}
	</script>
	<script>
		class MessageBar {
			node = null;
			offline = true;
			show = "";
			isroom = false;
			timestamp = "";
			stat = ""
			constructor(node, offline, isroom, show) {
				this.node = node;
				this.offline = offline;
				this.isroom = isroom;
				this.show = show;
			}
			tomsg() {
				let u = timNodeMap.get(this.node);
				let cover = u.cover;
				if (this.offline && !this.isroom) {
					cover = cover+"/g/1"
				}
				let statusshow = ""
				let onlineshow = ""
				if (this.isroom) {
					statusshow = "<h6 style='font-size: smaller;font-weight: bolder;'>群组&#128246;</h6>";
				} else if (this.offline) {
					statusshow = "<h6 style='font-size: smaller;color:gainsboro'>[离线请留言]</h6>"
				} else {
					onlineshow = "chat-user-online"
					statusshow = "<h6 style='font-size: 18px;color:chartreuse;font-weight: bolder;'>在线 " + this.show + "</h6>";
				}
				var div = document.createElement('div');
				let fs = '<a  onclick="peer(\'' + this.node + '\',' + this.isroom + ');" class="list-group-item">'
					+ '<div class="d-flex" class="' + onlineshow + '">'
					+ '<img src="' + cover + '" width="42" height="42"  class="rounded-circle" alt="">'
					+ '<div class="flex-grow-1 ms-2">'
					+ '<h6 class="mb-0 chat-title">' + statusshow + '</h6>'
					+ '<span class="mb-0 chat-msg" style="font-size: smaller;">' + u.name + '</span>'
					+ '</div>'
					+ '<div class="chat-time"><div id="bartime_' + this.node + '">' + this.timestamp + '</div><div id="stat_' + this.node + '">' + this.stat + '</div></div>'
					+ '</div>'
					+ '</a>';
				div.innerHTML = fs;
				return div;
			}

			updatetime(timestamp) {
				let bt = document.getElementById("bartime_" + this.node);
				this.timestamp = timestamp;
				if (!isEmpty(bt)) {
					bt.innerHTML = timestamp;
				} else {
					console.log(this)
				}
			}

			updatestat(statshow) {
				let stat = document.getElementById("stat_" + this.node)
				this.stat = statshow;
				if (!isEmpty(stat)) {
					stat.innerHTML = statshow
				}
			}
		}
	</script>
	<script>
		sleep(5).then(() => modalLogin());
		var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
		var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
			return new bootstrap.Popover(popoverTriggerEl)
		})
	</script>
	<script>
		const em = ["😀", "😁", "😂", "🤣", "😃", "😄", "😅", "😆", "😉", "😊", "😋",
			"😎", "😍", "😘", "🥰", "😗", "😙", "🥲", "😚", "🙂", "🤗", "🤩", "🤔",
			"🫡", "🤨", "😐", "😑", "😶", "🫥", "😶‍🌫️", "🙄", "😏", "😣", "😥", "😮", "🤐",
			"😯", "😪", "😫", "🥱", "😴", "😌", "😛", "😜", "😝", "🤤", "😒", "🫠", "🙃",
			"🫤", "😔", "😓", "🤑", "😲", "☹️", "🙁", "😖", "😞", "💩", "👻", "👽", "🦎",
			"🐊", "🐢", "🐍", "🐿️", "🐘", "🦐", "🦑", "🐙", "❤️", "💔", "❤️‍🔥", "❤️‍🩹", "💕",
			"💞", "💌", "💢", "💥", "💤", "💦", "🤝", "👏", "👍", "👎", "👊", "✊", "✌",
			"👌", "✋", "🙏", "☝", "👆", "👇", "👈", "👉", "🖕", "🤘", "🖐", "🤟", "🖖",
			"🌹", "🌷", "🥀", "💐", "🍎", "🎂", "⚽️", "0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣",
			"6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"];
		let emj = document.getElementById("emoji");
		for (const e of em) {
			emj.innerHTML = emj.innerHTML + '<a href="javascript:;" style="font-size: large;" onclick="addemoji(this)">' + e + '</a>'
		}
		const gcv = ["/img/001.jpg", "/img/002.jpg", "/img/003.jpg", "/img/004.jpg", "/img/005.jpg", "/img/006.jpg", "/img/007.jpg", "/img/008.jpg", "/img/009.jpg", "/img/010.jpg"]
		let groupcoverlist = document.getElementById("groupcoverlist");
		for (const e of gcv) {
			groupcoverlist.innerHTML = groupcoverlist.innerHTML + '<img width="50px" height="50px" onclick="selectgcover(this)" src="' + e + '" />'
		}
		function selectgcover(t) {
			document.getElementById("groupcover").src = t.getAttribute("src");
			document.getElementById("groupcover2").value = t.getAttribute("src");
		}

		const vgcv = ["/img/101.jpg", "/img/102.jpg", "/img/103.jpg", "/img/104.jpg", "/img/105.jpg", "/img/106.jpg", "/img/107.jpg", "/img/108.jpg", "/img/109.jpg", "/img/110.jpg"]
		let groupcoverlist_v = document.getElementById("groupcoverlist_v");
		for (const e of vgcv) {
			groupcoverlist_v.innerHTML = groupcoverlist_v.innerHTML + '<img width="50px" height="50px" onclick="selectvgcover(this)" src="' + e + '" />'
		}
		function selectvgcover(t) {
			document.getElementById("groupcover_v").src = t.getAttribute("src");
			document.getElementById("groupcover2_v").value = t.getAttribute("src");
		}

		const cv = ["/img/1.jpg", "/img/2.jpg", "/img/3.jpg", "/img/4.jpg", "/img/5.jpg", "/img/6.jpg", "/img/7.jpg", "/img/8.jpg"
			, "/img/9.jpg", "/img/10.jpg", "/img/11.jpg", "/img/12.jpg", "/img/13.jpg", "/img/14.jpg", "/img/15.jpg", "/img/16.jpg", "/img/17.jpg"
			, "/img/18.jpg", "/img/19.jpg", "/img/20.jpg", "/img/21.jpg", "/img/22.jpg", "/img/23.jpg", "/img/24.jpg", "/img/25.jpg", "/img/26.jpg"
			, "/img/27.jpg", "/img/28.jpg", "/img/29.jpg", "/img/30.jpg", "/img/31.jpg", "/img/32.jpg", "/img/33.jpg", "/img/34.jpg", "/img/35.jpg"
			, "/img/36.jpg", "/img/37.jpg", "/img/38.jpg", "/img/39.jpg", "/img/40.jpg", "/img/41.jpg", "/img/42.jpg", "/img/43.jpg", "/img/44.jpg", "/img/45.jpg"]
		let headcoverlist = document.getElementById("headcoverlist");
		let rheadcoverlist = document.getElementById("rheadcoverlist");
		for (const e of cv) {
			headcoverlist.innerHTML = headcoverlist.innerHTML + '<img width="40px" height="40px" onclick="selectcover(this)" src="' + e + '" />'
			rheadcoverlist.innerHTML = rheadcoverlist.innerHTML + '<img width="40px" height="40px" onclick="selectrcover(this)" src="' + e + '" />'
		}
		function selectcover(t) {
			document.getElementById("headcover").src = t.getAttribute("src");
			document.getElementById("headcover2").value = t.getAttribute("src");
			document.getElementById("coverinputs").value = "";
		}
		function selectrcover(t) {
			document.getElementById("rheadcover").src = t.getAttribute("src");
			document.getElementById("rheadcover2").value = t.getAttribute("src");
		}
		function selectCover(t) {
			let f = document.getElementById("coverinputs")
			if (f.files.length > 0) {
				reads(f.files[0]);
			}
		}
		function reads(fil) {
			let reader = new FileReader();
			reader.readAsDataURL(fil);
			reader.onload = function () {
				document.getElementById("headcover").src = reader.result;
			};
		}
		$(document).ready(function () {
			$("#coverinputs").change(function () {
				if (this.files.length > 0) {
					reads(this.files[0]);
				}
			});
		});
	</script>
	<script>
		var bigDataStreamEvent = new Map();
		var recvfileMap = new Map();
		var sendfileMap = new Map();
		const liveEvent = (data, node) => {
			if (data.length < 1000) {
				if (data.length == 1 && data[0] == 0) {
					endvideostreamdivshow();
				}
				return
			}
			if (vjoin4videostream) {
				vjoin4videostream.AddVideoUint8Array(ungzip(data))
			}
		}

		const roomliveEvent = (data, node) => {
			if (data.length < 1000) {
				return
			}
			let m = mulMediaroommap.get(node)
			if (!isEmpty(m)) {
				m.addstream(data)
			}
		}

		const recvfileEvent = (data, node) => {
			if (data.length == 1 && data[0] == 0) {
				let fn = recvfileMap.get(node)
				if (!isEmpty(fn)) {
					let t = mergeUint8Arrays(fn.body);
					saveFile(t, fn.filename)
					recvfileMap.delete(node);
					showsendToast();
					document.getElementById("sendfileshow").innerHTML = "<h6 style='font-size:smaller;'>[" + fn.filename + "]已经接收完毕</h6>"
				}
			} else {
				let fn = recvfileMap.get(node)
				let bt = '<button class="btn btn-light" onclick="cancelrecvfile(\'' + node + '\')">取消接收</button>'
				let rateid = document.getElementById("rateid")
				if (rateid.innerHTML != bt) {
					rateid.innerHTML = bt
				}
				if (!isEmpty(fn)) {
					fn.add(ungzip(data))
					let r = fn.getsize / fn.size;
					document.getElementById("sendfileshow").innerHTML = "<h6 style='font-size:smaller;'>" + fn.filename + "已经接收:" + Math.floor(100 * r) + "%</h6>";
				}
			}
		}

		function cancelrecvfile(vnode) {
			let fn = recvfileMap.get(vnode);
			if (!isEmpty(fn)) {
				recvfileMap.delete(vnode);
				document.getElementById("sendfileshow").innerHTML = "";
				document.getElementById("rateid").innerHTML = "";
				tc.StreamToUser(fn.fnode, vnode, 0, 15);
			}
		}

		class fileBean {
			filename = null;
			size = null;
			getsize = 0
			body = [];
			fnode = null;
			constructor(fname, size, fnode) {
				this.filename = fname;
				this.size = size;
				this.fnode = fnode;
			}
			add(data) {
				this.getsize = this.getsize + data.length;
				this.body.push(data);
			}
		}

		class sendFileBean {
			file = null;
			tnode = null;
			vnode = null;
			constructor(file, vnode, tnode) {
				this.file = file;
				this.vnode = vnode;
				this.tnode = tnode;
			}
		}
	</script>

	<script>
		var mulMediaroommap = new Map()
		const storeVideoRoom = (unode, vnode) => {
			if (!mulMediaroommap.has(vnode)) {
				let mv = new mulvideo(mutlivideostreamlist, vnode, unode)
				mulMediaroommap.set(vnode, mv)
				mv.open()
			}
		}
		class mulvideo {
			supervideodiv = null;
			vjoin4videostream = null;
			subVnode = null;
			unode = null;
			videoroomdiv = null;
			w = 1
			constructor(supervideodiv, subVnode, unode) {
				this.supervideodiv = supervideodiv;
				this.subVnode = subVnode;
				this.unode = unode;
			}
			open() {
				this.videoroomdiv = createVideoroomDiv(timNodeMap.get(this.unode).name)
				this.supervideodiv.appendChild(this.videoroomdiv);
				// let newv = document.createElement("video");
				// this.videoroomdiv.appendChild(newv)
				this.vjoin4videostream = new VideoJoin(this.videoroomdiv);
				this.vjoin4videostream.style = "max-width:250px;max-height: 250px;"
				this.vjoin4videostream.modeRealtime = false;
				this.vjoin4videostream.modeSpeedRateup = true;
				tc.VirtualroomSubBinary(this.subVnode)
				this.watch(0)
			}
			addstream(data) {
				if (isEmpty(this.videoroomdiv)) {
					this.open()
				}
				this.w++;
				this.vjoin4videostream.AddVideoUint8Array(ungzip(data))
			}
			notify() {
				if (isEmpty(this.videoroomdiv)) {
					this.open()
				}
				this.w++;
			}
			close() {
				try {
					this.vjoin4videostream.Clear();
					this.videoroomdiv.innerHTML = "";
					this.supervideodiv.removeChild(this.videoroomdiv)
					this.videoroomdiv = null;
				} catch (err) { }
			}
			watch(a) {
				if (this.w <= a) {
					this.close()
				} else {
					a = this.w;
					sleep(2000).then(() => this.watch(a + 1))
				}
			}
		}

		function createVideoroomDiv(name) {
			let h6 = document.createElement("h6")
			h6.setAttribute("class", "card-header")
			h6.innerHTML = '&#127909;' + name
			let videoroomdiv = document.createElement("div")
			videoroomdiv.setAttribute("class", "card d-flex col-6 col-sm-6 col-md-6 col-lg-3 col-xl-2 col-xxl-1");
			videoroomdiv.appendChild(h6);
			return videoroomdiv
		}

		function createImgroomDiv(name, cover) {
			let h6 = document.createElement("h6");
			h6.setAttribute("class", "card-header");
			h6.innerHTML = '&#128222;' + name
			let imgdiv = document.createElement("div")
			imgdiv.setAttribute("class", "card d-flex col-6 col-sm-6 col-md-6 col-lg-3 col-xl-2 col-xxl-1");
			imgdiv.appendChild(h6);
			let img = document.createElement("img");
			img.src = cover
			img.setAttribute("style", "width: 100%; height: auto;")
			imgdiv.appendChild(img);
			return imgdiv
		}
	</script>

	<script>
		const storeAudioRoom = (unode, vnode) => {
			if (!mulMediaroommap.has(vnode)) {
				let mv = new mulaudio(mutlivideostreamlist, vnode, unode)
				mulMediaroommap.set(vnode, mv)
				mv.open()
			}
		}
		class mulaudio {
			supervideodiv = null;
			vjoin4audiostream = null;
			subVnode = null;
			unode = null;
			imgdiv = null;
			w = 1
			constructor(supervideodiv, subVnode, unode) {
				this.supervideodiv = supervideodiv;
				this.subVnode = subVnode;
				this.unode = unode;
			}
			open() {
				this.imgdiv = createImgroomDiv(timNodeMap.get(this.unode).name, timNodeMap.get(this.unode).cover)
				this.supervideodiv.appendChild(this.imgdiv);
				this.vjoin4audiostream = new AudioJoin();
				this.vjoin4audiostream.style = "max-width:250px;max-height: 250px;"
				this.vjoin4audiostream.modeRealtime = false;
				this.vjoin4audiostream.modeSpeedRateup = true;
				tc.VirtualroomSubBinary(this.subVnode)
				this.watch(0)
			}
			addstream(data) {
				if (isEmpty(this.imgdiv)) {
					this.open()
				}
				this.w++;
				this.vjoin4audiostream.AddAudioUint8Array(ungzip(data))
			}
			close() {
				try {
					this.vjoin4audiostream.Clear();
					this.imgdiv.innerHTML = "";
					this.supervideodiv.removeChild(this.imgdiv)
					this.imgdiv = null;
				} catch (err) { }
			}
			notify() {
				if (isEmpty(this.imgdiv)) {
					this.open()
				}
				this.w++;
			}
			watch(a) {
				if (this.w <= a) {
					this.close()
				} else {
					a = this.w;
					sleep(2000).then(() => this.watch(a + 1))
				}
			}
		}

		class notify {
			count = 0
			constructor() { }
			send(account, vnode) {
				let c = ++this.count
				this.watch(c, account, vnode)
			}
			watch(c, account, vnode) {
				if (c < this.count) {
					return
				} else {
					tc.StreamToRoom(account, vnode, 0, 34)
					sleep(800).then(() => this.watch(c, account, vnode))
				}
			}
			stop() {
				++this.count
			}
		}
		var notifyroom = new notify()
	</script>

	<script>
		document.getElementById('updateSelfcover').addEventListener('click', function () {
			document.getElementById('coverinputs').click();
		});
		function isPageHidden() {
			if (typeof document.hidden !== "undefined") {
				return document.hidden
			} else {
				switch (document.visibilityState) {
					case 'visible':
						return false
					case 'hidden':
						return true;
					case 'prerender':
						return false
					case 'unloaded':
						return true;
				}
			}
			return false;
		}
		var flashTitleIng = false;
		function flashTitle(message) {
			if (flashTitleIng) {
				return
			}
			let faviconLink = document.querySelector('link[rel="shortcut icon"], link[rel="icon"]');
			let faviconLinkHref = faviconLink.href;
			let originalTitle = "webtim";
			_flashTitle(message, originalTitle, faviconLinkHref, faviconLink)
		}

		function _flashTitle(message, originalTitle, faviconLinkHref, faviconLink) {
			if (!isPageHidden()) {
				flashTitleIng = false
				document.title = originalTitle;
				faviconLink.href = faviconLinkHref;
				return
			}
			flashTitleIng = true
			document.title = document.title === originalTitle ? message : originalTitle;
			faviconLink.href = faviconLink.href === faviconLinkHref ? "/img/icon.png" : faviconLinkHref
			sleep(10).then(() => _flashTitle(message, originalTitle, faviconLinkHref, faviconLink))
		}
	</script>
</body>

</html>